<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Devarshi Shimpi</title>
    <description>The latest articles on DEV Community by Devarshi Shimpi (@devarshishimpi).</description>
    <link>https://dev.to/devarshishimpi</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F838856%2Fcfa52442-fee6-4fa6-96c3-78c089e5d2e7.jpg</url>
      <title>DEV Community: Devarshi Shimpi</title>
      <link>https://dev.to/devarshishimpi</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/devarshishimpi"/>
    <language>en</language>
    <item>
      <title>How the Bitcoin Network Actually Works</title>
      <dc:creator>Devarshi Shimpi</dc:creator>
      <pubDate>Sun, 26 Jul 2026 00:14:33 +0000</pubDate>
      <link>https://dev.to/devarshishimpi/how-the-bitcoin-network-actually-works-943</link>
      <guid>https://dev.to/devarshishimpi/how-the-bitcoin-network-actually-works-943</guid>
      <description>&lt;p&gt;Bitcoin gets called a lot of things: digital gold, internet money, a scam, a revolution. This guide skips all of that. It is not about what Bitcoin is &lt;em&gt;for&lt;/em&gt;. It is about what Bitcoin &lt;em&gt;is&lt;/em&gt;: the parts, how they fit, and why they work.&lt;/p&gt;

&lt;p&gt;We are going to build the idea up in layers, because that is genuinely how Bitcoin is put together. Each layer sits on top of the one below it. If you skip a layer, the ones above look like magic. If you take them in order, the magic turns into something more satisfying: plain engineering.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzr9x2jf46pbggne1jfw1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzr9x2jf46pbggne1jfw1.png" alt="btc-diagram-foundation-infra" width="800" height="343"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A quick note on numbers.&lt;/strong&gt; The &lt;em&gt;rules&lt;/em&gt; in this guide (how often blocks appear, the coin limit, how a block is structured) are fixed by the software and do not change. The &lt;em&gt;live figures&lt;/em&gt; (how much computing power is on the network, how busy it is, the price) change all the time. Where I mention one of those, treat it as "roughly true in mid-2026" and check a live site for today's value.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we are actually looking at
&lt;/h2&gt;

&lt;p&gt;Here is the one-sentence version of the whole thing:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Bitcoin is a way for a huge group of strangers, who do not trust each other and have no boss, to agree on a single shared list of who owns what.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That is harder than it sounds. Normally, when a group needs to agree on something, someone is in charge. A bank keeps the official record of your balance. A referee makes the call. Bitcoin has no one in charge, and some of the people taking part may be actively trying to cheat. Somehow it still ends up with one agreed answer.&lt;/p&gt;

&lt;p&gt;By the end of this guide you should be able to explain, in your own words, why a coin cannot be spent twice, why sending Bitcoin costs a fee, why the mining difficulty keeps adjusting up and down, why "running a node" and "mining" are two completely different jobs, and what actually happens in the roughly ten minutes between tapping &lt;strong&gt;send&lt;/strong&gt; and the payment becoming permanent.&lt;/p&gt;

&lt;h2&gt;
  
  
  The one problem Bitcoin was built to solve
&lt;/h2&gt;

&lt;p&gt;Everything starts here, so it is worth slowing down.&lt;/p&gt;

&lt;p&gt;The problem is called &lt;strong&gt;double spending&lt;/strong&gt;, and it only exists for digital money.&lt;/p&gt;

&lt;p&gt;Think about a physical $20 bill. When you hand it to a shop, you no longer have it. The object moved from your hand to theirs. You physically cannot be in two shops spending the same bill at the same time.&lt;/p&gt;

&lt;p&gt;Now think about anything digital: a photo, a song, a file. Copying it is free and perfect. You can send the exact same photo to a thousand people at once, and each copy is flawless. That is great for photos. It is a disaster for money. If a "coin" were just a file, you could send the same coin to a thousand different people, and each one would see a perfectly real coin. Only later would everyone realise they were all paid with the same money.&lt;/p&gt;

&lt;p&gt;The usual fix is to put someone in charge. A bank keeps one official list. When you pay, the bank subtracts from your row and adds to someone else's row. Because there is only one list, controlled by one company, the same dollar cannot be in two places. This works, and most of the world runs on it. But it comes with a cost. You have to trust that company. It can freeze you, reverse payments, get hacked, go under, or simply charge you for the service.&lt;/p&gt;

&lt;p&gt;So the question that led to Bitcoin was very specific:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Can you keep one official list of who owns what, &lt;strong&gt;without anyone being in charge of the list?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Two pieces of math hold up everything else
&lt;/h2&gt;

&lt;p&gt;Almost every surprising thing about Bitcoin comes from one of two tools. Neither was invented for Bitcoin. Both are decades old and used all over the internet.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tool 1: the fingerprint machine (a hash)
&lt;/h3&gt;

&lt;p&gt;A &lt;strong&gt;hash function&lt;/strong&gt; is like a machine that takes anything you feed it (a word, a file, an entire book) and spits out a short jumble of characters. Bitcoin uses one called &lt;strong&gt;SHA-256&lt;/strong&gt;, whose output is always 64 characters long. Feed in a single letter or the complete works of Shakespeare, and you still get exactly 64 characters back. People often call this output a &lt;strong&gt;fingerprint&lt;/strong&gt;, and that is the word I will use.&lt;/p&gt;

&lt;p&gt;Three things make it special, and the whole security of Bitcoin leans on them:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Same input, same fingerprint, every time.&lt;/strong&gt; Anyone, anywhere, feeding in the same thing gets the same result. No coordination needed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Change the input a tiny bit, and the fingerprint changes completely.&lt;/strong&gt; There is no "close." Change one letter and about half the output flips, with no pattern connecting the two.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You cannot run it backwards.&lt;/strong&gt; Given a fingerprint, there is no way to figure out what was fed in, other than guessing. And there are so many possible fingerprints that guessing is hopeless. (For scale: the number of possible outputs is far larger than the number of atoms on Earth.)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Point 2 is the one people underestimate, so here it is made concrete. These two inputs differ by a single letter:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"Bitcoin"  -&amp;gt;  b4056df6691f8dc72e56302ddad345d65fead3ead9299609a826e2344eb63aa4
"bitcoin"  -&amp;gt;  6b88c087247aa2f07ee1c5956b8e1a9f4c7f892a70e324f1bb3d161e05ca107b
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One lowercase letter, and 62 of the 64 characters came out different. Underneath them, about half of the 256 bits flipped. There is no pattern connecting the two results.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Try it live:&lt;/strong&gt; &lt;a href="https://devarshi.dev/blog/how-the-bitcoin-network-actually-works#avalanche-effect" rel="noopener noreferrer"&gt;type any word and watch the diff highlight, character by character&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This matters a lot later. Because there is no "getting warmer," you cannot be clever and steer a fingerprint toward a shape you want. Your only option is to try, check, and try again. Remember that.&lt;/p&gt;

&lt;p&gt;A good way to build intuition is to feel it yourself.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Try it live:&lt;/strong&gt; &lt;a href="https://devarshi.dev/blog/how-the-bitcoin-network-actually-works#hash-playground" rel="noopener noreferrer"&gt;a SHA-256 playground that rehashes on every keystroke&lt;/a&gt;. Type something, delete one character, and watch the whole 64-character output rearrange.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;One more detail for the curious: Bitcoin usually runs this machine &lt;em&gt;twice&lt;/em&gt; in a row, taking the fingerprint of the fingerprint. You do not need to remember why. Just know that when you hear "the block's ID" or "the transaction ID," that is one of these fingerprints.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tool 2: a lock only you can close, that anyone can check
&lt;/h3&gt;

&lt;p&gt;The second tool answers a different question: how do you prove you are allowed to spend a coin, without giving away a secret that would let anyone else spend it too?&lt;/p&gt;

&lt;p&gt;The answer is a matched pair of keys:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;strong&gt;private key&lt;/strong&gt;, which is basically a giant secret random number that only you know.&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;public key&lt;/strong&gt;, which is calculated from the private key and which you can share freely.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Like the fingerprint machine, this only runs one way. It is easy to make the public key from the private key, and effectively impossible to go backwards.&lt;/p&gt;

&lt;p&gt;With this pair you can do two things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Sign.&lt;/strong&gt; Using your private key, you can stamp a specific message (a payment) with a unique signature. The signature only works for that key and that exact message.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Verify.&lt;/strong&gt; Anyone with your public key can check that the signature is real, which proves &lt;em&gt;you&lt;/em&gt; made it, without ever seeing your private key.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A helpful picture: think of a special wax seal that only your ring can press, but that anyone in the world can recognise on sight. You never hand over the ring. You just stamp things, and everyone can confirm the stamp is yours.&lt;/p&gt;

&lt;p&gt;This is the entire idea of owning Bitcoin. A coin is not sitting "inside" a wallet like a file in a folder. To own Bitcoin simply means you know a private key that can produce a valid signature to move that coin. Lose the key and the coins are stuck forever. There is no support line and no reset button. That is what people mean by the phrase "not your keys, not your coins."&lt;/p&gt;

&lt;h2&gt;
  
  
  From a secret number to an address
&lt;/h2&gt;

&lt;p&gt;You have probably seen a Bitcoin address, one of those long strings starting with &lt;code&gt;bc1&lt;/code&gt;. Here is where it comes from, and it is just the two tools from the previous section (the fingerprint machine and the key pair) chained together.&lt;/p&gt;

&lt;p&gt;The path runs in one direction only:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;private key  -&amp;gt;  public key  -&amp;gt;  address&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Each arrow is easy to walk forwards and practically impossible to walk backwards. You make the public key from the private key using the key math. Then you make the address by taking the fingerprint of the public key.&lt;/p&gt;

&lt;p&gt;Because of that one-way street, two everyday facts fall out:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Sharing your address is safe.&lt;/strong&gt; It is downstream of everything secret, so it gives nothing away. People can send you money, and that is all.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sharing your private key is a catastrophe.&lt;/strong&gt; It is the thing at the very start of the chain. Anyone who has it can sign, which means they can spend everything.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You may notice addresses start with different prefixes. A quick key:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Starts with&lt;/th&gt;
&lt;th&gt;Technical name&lt;/th&gt;
&lt;th&gt;Plain meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;1...&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;P2PKH (legacy)&lt;/td&gt;
&lt;td&gt;the original address style&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;3...&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;P2SH&lt;/td&gt;
&lt;td&gt;often used for shared or multi-key setups&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;bc1q...&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;native SegWit v0 (Bech32)&lt;/td&gt;
&lt;td&gt;cheaper, more efficient addresses, from the 2017 SegWit upgrade&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;bc1p...&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Taproot / SegWit v1 (Bech32m)&lt;/td&gt;
&lt;td&gt;the newest style, from the 2021 Taproot upgrade&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;They all do the same basic job: they are a safe-to-share destination for money. As a quick rule of thumb, Taproot addresses commonly start with &lt;code&gt;bc1p&lt;/code&gt;, and the older native SegWit addresses start with &lt;code&gt;bc1q&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Blocks, and why they form a chain
&lt;/h2&gt;

&lt;p&gt;Now we can build the record itself.&lt;/p&gt;

&lt;p&gt;A &lt;strong&gt;block&lt;/strong&gt; is simply a batch of transactions bundled together, plus a small label on top called the &lt;strong&gt;header&lt;/strong&gt;. Think of a block as one page in a shared notebook. The page has a body (the list of payments) and a heading (the summary info). New pages are added roughly every ten minutes, and each page can hold a few thousand payments.&lt;/p&gt;

&lt;p&gt;The header is small but it is where the clever part lives. The whole thing fits in 80 bytes:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Field&lt;/th&gt;
&lt;th&gt;Size&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Version&lt;/td&gt;
&lt;td&gt;4 bytes&lt;/td&gt;
&lt;td&gt;which rules this block follows&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Previous block's fingerprint&lt;/td&gt;
&lt;td&gt;32 bytes&lt;/td&gt;
&lt;td&gt;the link that chains blocks together&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Merkle root&lt;/td&gt;
&lt;td&gt;32 bytes&lt;/td&gt;
&lt;td&gt;one fingerprint summing up every transaction&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Timestamp&lt;/td&gt;
&lt;td&gt;4 bytes&lt;/td&gt;
&lt;td&gt;roughly when the block was made&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Target ("bits")&lt;/td&gt;
&lt;td&gt;4 bytes&lt;/td&gt;
&lt;td&gt;how hard the puzzle is right now&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Nonce&lt;/td&gt;
&lt;td&gt;4 bytes&lt;/td&gt;
&lt;td&gt;the number miners keep changing to win&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Notice that two fields eat 64 of the 80 bytes, and both of them are fingerprints. That tells you what a header is really for.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Try it live:&lt;/strong&gt; &lt;a href="https://devarshi.dev/blog/how-the-bitcoin-network-actually-works#block-header" rel="noopener noreferrer"&gt;tap through the header drawn to scale&lt;/a&gt;, where each field's width matches its byte count.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The single most important field is &lt;strong&gt;the previous block's fingerprint&lt;/strong&gt;. Every block's header contains the fingerprint of the block right before it. That one detail is what turns a pile of separate pages into a connected &lt;strong&gt;chain&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F05c0hbbl6pj1jsc4pn0f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F05c0hbbl6pj1jsc4pn0f.png" alt="btc-diagram-fingerprint-blocks" width="799" height="262"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here is why that chain is so powerful. Imagine a cheater wants to secretly change a payment in an old block. The moment they change anything in that old block, its fingerprint changes (that is the fingerprint machine from earlier). But the &lt;em&gt;next&lt;/em&gt; block was carrying the &lt;em&gt;old&lt;/em&gt; fingerprint. So now the next block does not match, and its own fingerprint would need to change too, which breaks the block after that, and so on all the way to the present.&lt;/p&gt;

&lt;p&gt;In other words, you cannot quietly edit page 400 of the notebook. Editing it visibly wrecks every page after it. To make the change stick, the cheater would have to redo every single page from 400 to now, faster than the rest of the world is adding new pages. The section on proof of work explains why that is effectively impossible.&lt;/p&gt;

&lt;h2&gt;
  
  
  Merkle trees: summing up thousands of payments with one fingerprint
&lt;/h2&gt;

&lt;p&gt;You may have spotted a field in the header called the &lt;strong&gt;Merkle root&lt;/strong&gt;. It sounds fancy. The idea is simple and genuinely elegant.&lt;/p&gt;

&lt;p&gt;A block might hold thousands of transactions. The header does not want to store all of them, but it does want a way to lock them in so none can be swapped out later. The trick is to squash all of them down into a single fingerprint, built like a knockout tournament.&lt;/p&gt;

&lt;p&gt;Pair up the transactions and take a fingerprint of each pair. Then pair up &lt;em&gt;those&lt;/em&gt; fingerprints and hash them. Keep going until only one fingerprint is left standing. That final winner is the &lt;strong&gt;Merkle root&lt;/strong&gt;, and it goes in the header.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frktpzlp7cd264gg3qo39.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frktpzlp7cd264gg3qo39.png" alt="btc-diagram-merkle-root" width="799" height="387"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Two nice things come out of this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Nothing can be swapped.&lt;/strong&gt; Change any single payment and its fingerprint changes, which changes its pair, which changes the root, which changes the whole block. The root locks everything in.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You can prove one payment is in a block without downloading the whole block.&lt;/strong&gt; This is how a phone wallet can confirm your payment landed without storing the entire history of Bitcoin. It only needs a short trail of fingerprints up the tree, not the thousands of other payments.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Coins are not a balance, they are more like cash
&lt;/h2&gt;

&lt;p&gt;This is the part that trips up almost everyone, because it works differently from a bank.&lt;/p&gt;

&lt;p&gt;Your bank account has a single number: your balance. When you spend, the number goes down. Bitcoin does not work that way. Bitcoin is more like a wallet full of actual bills and coins of odd sizes.&lt;/p&gt;

&lt;p&gt;In Bitcoin, when you receive money, you receive it as a distinct chunk, like being handed a specific bill. The proper name for one of these unspent chunks is an &lt;strong&gt;unspent transaction output&lt;/strong&gt;, usually shortened to &lt;strong&gt;UTXO&lt;/strong&gt;. You do not need the jargon. Just picture bills of random sizes sitting in your wallet.&lt;/p&gt;

&lt;p&gt;Now, here is the cash-like rule: &lt;strong&gt;you cannot spend part of a bill.&lt;/strong&gt; If you have a 0.4 coin chunk and a 0.25 coin chunk, and you want to pay someone 0.6, you hand over both chunks, the seller keeps 0.6, and the leftover comes back to you as &lt;strong&gt;change&lt;/strong&gt;, as a brand new chunk. Written out, that payment looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;coins going in                0.65 BTC     a 0.4 coin plus a 0.25 coin
to the seller                 0.6 BTC      the payment
back to you, as a new coin    0.049 BTC    your change
left for the miner            0.001 BTC    the fee
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Try it live:&lt;/strong&gt; &lt;a href="https://devarshi.dev/blog/how-the-bitcoin-network-actually-works#utxo-flow" rel="noopener noreferrer"&gt;pick which coins to spend and drag the payment amount&lt;/a&gt; to watch the change and the fee resolve.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The tiny bit that does not come back as change is the &lt;strong&gt;fee&lt;/strong&gt;, which the miner who includes your payment gets to keep. There is more on fees in the section on the waiting room below.&lt;/p&gt;

&lt;p&gt;Two everyday consequences of this design:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Your "balance" is not stored anywhere.&lt;/strong&gt; Your wallet app just scans the record, finds every chunk that belongs to your keys, and adds them up to show you a number. Nobody wrote that number down.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Privacy is fiddly.&lt;/strong&gt; Because change comes back as a new chunk, following the trail of chunks is how blockchain analysts try to trace activity.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Two different jobs: nodes and miners
&lt;/h2&gt;

&lt;p&gt;People mix these up constantly. They are separate roles, and most participants only do one of them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Nodes are the referees.&lt;/strong&gt; A node is a computer running the Bitcoin software that keeps a full copy of the record and checks every single rule. Is this signature valid? Does this person actually own the coins they are trying to spend? Is this block properly formed? If anything breaks a rule, the node rejects it and refuses to pass it on. There are tens of thousands of these referees around the world, and crucially, &lt;em&gt;anyone can run one on ordinary hardware.&lt;/em&gt; This is the real backbone of Bitcoin. The rules are enforced by thousands of independent copies, not by any authority.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Miners are the record keepers who compete for the right to add the next page.&lt;/strong&gt; Their job is to bundle waiting transactions into a new block and win the right to add it. This takes serious specialised equipment and a lot of electricity. The next section is all about what they are actually doing.&lt;/p&gt;

&lt;p&gt;The key thing to take away: &lt;strong&gt;miners propose, nodes decide.&lt;/strong&gt; A miner can build any block it likes, but if that block breaks the rules, every honest node simply throws it away and the miner wasted its effort for nothing. Power in Bitcoin is split on purpose.&lt;/p&gt;

&lt;h2&gt;
  
  
  Proof of work: the guessing game you cannot cheat
&lt;/h2&gt;

&lt;p&gt;This is the heart of Bitcoin, and thanks to the two tools from earlier it is now easy to explain.&lt;/p&gt;

&lt;p&gt;To add the next block, a miner has to win a guessing game. Remember the header has a field called the &lt;strong&gt;nonce&lt;/strong&gt;, which is just a number the miner is free to change. The game is this: find a nonce so that when you take the fingerprint of the whole header, the result is small enough.&lt;/p&gt;

&lt;p&gt;To be precise: a fingerprint is really just a very large number written in hex. The header also carries a &lt;strong&gt;target&lt;/strong&gt; value (packed into a small field called &lt;code&gt;nBits&lt;/code&gt;). A block only counts as valid if its fingerprint, read as a number, comes out &lt;strong&gt;below that target&lt;/strong&gt;. The lower the target, the fewer fingerprints qualify, and the harder the puzzle. The easy way to picture "below the target" is "the fingerprint has to start with a certain number of zeros," and that is the mental model I will use, but keep in mind the real rule is the numeric comparison against the target.&lt;/p&gt;

&lt;p&gt;That is it. Because of the fingerprint machine's "no getting warmer" rule from earlier, there is no smart way to find such a nonce. You cannot calculate it. You cannot nudge the number in the right direction. You can only try a nonce, take the fingerprint, check if it has enough leading zeros, and if not, add one and try again. Over and over, billions of times per second.&lt;/p&gt;

&lt;p&gt;You can feel exactly how this works by doing it yourself. Choose how many leading zeros you want, hit start, and your browser runs the same dumb loop a real miner does, just far slower. On my machine, asking for 4 leading zeros took 99,664 guesses and about 1.7 seconds. Ask for 5 and the work multiplies by roughly sixteen.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Try it live:&lt;/strong&gt; &lt;a href="https://devarshi.dev/blog/how-the-bitcoin-network-actually-works#hash-grinder" rel="noopener noreferrer"&gt;mine a block by hand, with real SHA-256&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now, why is this pointless-looking effort actually useful? Because of a beautiful lopsidedness:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Very hard to do, instant to check.&lt;/strong&gt; Finding a winning nonce takes the entire network huge effort. But &lt;em&gt;checking&lt;/em&gt; someone else's winning block takes any referee a split second: take the fingerprint once, count the zeros, done. Making the proof is expensive. Verifying the proof is free.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lying costs real money.&lt;/strong&gt; A winning block represents real electricity that got burned. To rewrite an old block, a cheater would have to burn all that electricity again, plus redo every block since, plus keep outrunning the entire rest of the world adding new blocks in real time, forever. It is not that cheating is banned. It is that cheating is wildly more expensive than playing fair.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That last point is the whole security model in one line: &lt;strong&gt;behaving honestly pays better than attacking.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Why almost nobody mines alone
&lt;/h3&gt;

&lt;p&gt;Winning a block is a lottery. If you mine by yourself, you might go months or years without ever winning, then win once, then nothing again. That wild unpredictability is unbearable for most operators trying to pay an electricity bill.&lt;/p&gt;

&lt;p&gt;So miners team up into &lt;strong&gt;pools&lt;/strong&gt;. A pool has thousands of miners all working on the same block and splitting any reward in proportion to how much work each one did. This turns a wild lottery into a steady, paycheck-like income. The downside is that it concentrates a lot of decision-making into a handful of large pools, which is one of the genuine worries about Bitcoin today.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keeping blocks ten minutes apart, forever
&lt;/h2&gt;

&lt;p&gt;Bitcoin aims for a new block roughly every ten minutes. But the amount of computing power aimed at the network is always changing. More miners join, better machines arrive, prices swing and some switch off. So how does the gap stay near ten minutes?&lt;/p&gt;

&lt;p&gt;The network &lt;strong&gt;retunes the difficulty&lt;/strong&gt; automatically. Every 2,016 blocks (which at ten minutes each works out to about two weeks), every node does the same simple check: did the last stretch of blocks arrive faster or slower than the ten-minute target? The difficulty then adjusts up or down accordingly. If the last period was mined too fast, the puzzle gets harder (the target drops, so more leading zeros are needed). If it was too slow, the puzzle gets easier. It can and regularly does move in both directions.&lt;/p&gt;

&lt;p&gt;A good mental image is a treadmill that speeds up as more runners jump on, so that a runner still crosses the line about every ten minutes no matter how many are racing. This is why you cannot make Bitcoin produce coins faster by throwing more machines at it. Add more power and the puzzle simply gets harder to match.&lt;/p&gt;

&lt;p&gt;Every node calculates this adjustment independently and arrives at the same answer, because they all follow the same rule on the same shared record. No committee, no announcement. Just math everyone runs.&lt;/p&gt;

&lt;h2&gt;
  
  
  New coins, the halving, and the 21 million limit
&lt;/h2&gt;

&lt;p&gt;So where do new coins come from, and who gets them?&lt;/p&gt;

&lt;p&gt;Whoever wins a block gets to include one special payment at the top that creates brand new coins out of nothing and pays them to the winner. This is the &lt;strong&gt;reward&lt;/strong&gt;, and it is the incentive that makes people spend money on mining in the first place. The winner also collects all the fees from the transactions in that block.&lt;/p&gt;

&lt;p&gt;Here is the famous twist. That reward &lt;strong&gt;cuts in half every four years&lt;/strong&gt; (more precisely, every 210,000 blocks). It started at 50 coins per block in 2009. It dropped to 25, then 12.5, then 6.25, and in 2024 to 3.125. This is called &lt;strong&gt;the halving&lt;/strong&gt;.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Halving&lt;/th&gt;
&lt;th&gt;Reward per block&lt;/th&gt;
&lt;th&gt;Coins in existence&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2009 (start)&lt;/td&gt;
&lt;td&gt;50&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2012&lt;/td&gt;
&lt;td&gt;25&lt;/td&gt;
&lt;td&gt;~10.50M&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2016&lt;/td&gt;
&lt;td&gt;12.5&lt;/td&gt;
&lt;td&gt;~15.75M&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2020&lt;/td&gt;
&lt;td&gt;6.25&lt;/td&gt;
&lt;td&gt;~18.38M&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2024&lt;/td&gt;
&lt;td&gt;3.125&lt;/td&gt;
&lt;td&gt;~19.69M&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2028&lt;/td&gt;
&lt;td&gt;1.5625&lt;/td&gt;
&lt;td&gt;~20.34M&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Each halving releases half of whatever is still left, so the total creeps toward &lt;strong&gt;21 million&lt;/strong&gt; without ever quite arriving. New coins trickle out more and more slowly until, sometime around the year 2140, the reward rounds down to zero and no new coins are ever made again.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Try it live:&lt;/strong&gt; &lt;a href="https://devarshi.dev/blog/how-the-bitcoin-network-actually-works#supply-curve" rel="noopener noreferrer"&gt;the supply curve, with tappable halving markers&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This fixed limit is a large part of why supporters compare Bitcoin to gold. There is a hard cap, and no one can vote to print more. As of mid-2026, roughly 20.06 million of the 21 million already exist. (That figure keeps ticking up slowly, so treat it as a snapshot and check a live source for today's exact number. The 21 million cap itself never moves.)&lt;/p&gt;

&lt;p&gt;There is an obvious question hiding here: if the reward eventually falls to zero, why would anyone keep mining? The intended answer is fees. As the new-coin reward shrinks, transaction fees are meant to become the main thing miners earn. Whether fees alone will be enough to keep the network secure that far out is a real and openly debated question. Nobody knows for certain.&lt;/p&gt;

&lt;h2&gt;
  
  
  The waiting room and why fees exist
&lt;/h2&gt;

&lt;p&gt;When you send Bitcoin, it does not go straight into a block. First it waits in the &lt;strong&gt;mempool&lt;/strong&gt; as an unconfirmed transaction until a miner picks it up. It is tempting to picture one single worldwide queue, but that is not quite how it works. Each node keeps its &lt;em&gt;own&lt;/em&gt; local pool of the valid, unconfirmed transactions it has heard about. These local pools overlap heavily, so they look similar, but there is no one official global waiting room. Think of it as many nearly-identical waiting rooms rather than a single one.&lt;/p&gt;

&lt;p&gt;Here is the catch: a block only has room for so many transactions. When more people want in than there is space, miners have to choose. And they choose the way you would expect anyone paid by tips to choose: &lt;strong&gt;they prefer the payments offering the highest fee for the space they take up.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Think of a miner as a taxi with limited room and a line of passengers, each waving a different tip. The driver naturally picks whoever pays the most per seat. If you attach a generous fee, you get into the next block. If you attach a stingy fee during a busy period, you wait, sometimes a long time.&lt;/p&gt;

&lt;p&gt;One important detail: the fee is not based on how much money you are sending. It is based on how much &lt;em&gt;space&lt;/em&gt; your transaction takes up in a block. That space is measured in &lt;strong&gt;virtual bytes&lt;/strong&gt; (also called weight), a size measure introduced by the 2017 SegWit upgrade. What miners actually rank by is the &lt;strong&gt;fee rate&lt;/strong&gt;: the fee divided by that virtual size. So a payment moving a fortune can be cheap if it is small and simple, and a payment moving pocket change can be expensive if it is large and complicated. You are paying for room on the page, not for the value being moved.&lt;/p&gt;

&lt;p&gt;Two features exist to help when you guess the fee wrong:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Bumping the fee&lt;/strong&gt; lets you resend a stuck transaction with a higher fee to jump the line.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Child pays for parent&lt;/strong&gt; lets a follow-up transaction attach a big fee to drag a stuck earlier one along with it, since a miner has to include the earlier one to collect the juicy later fee.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What happens when two miners win at once
&lt;/h2&gt;

&lt;p&gt;Sometimes two miners find a valid block at nearly the same moment, in different parts of the world. For a short while, the network disagrees. Part of it heard about block A, part of it heard about block B. This is called a &lt;strong&gt;fork&lt;/strong&gt;, and it is normal and expected, not a crisis.&lt;/p&gt;

&lt;p&gt;The tie does not last. Miners keep working, and soon someone builds the &lt;em&gt;next&lt;/em&gt; block on top of one of the two. Say the next block lands on top of A. Now the "A" side of the record is longer, and the rule everyone follows is simple: &lt;strong&gt;the version with the most work built on it wins.&lt;/strong&gt; Everyone switches to the longer branch. Block B, the loser, is abandoned, and its transactions (which are almost always also in A) go back into the waiting room to be included normally.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fokueodm42ca8q6awsa5e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fokueodm42ca8q6awsa5e.png" alt="btc-diagram-blocks" width="800" height="326"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is exactly why people say to wait for a few &lt;strong&gt;confirmations&lt;/strong&gt; on a big payment. One confirmation means your payment made it into a block. Each additional block stacked on top makes it exponentially harder to ever reverse, because a cheater would have to redo that block and all the ones after it faster than the whole network. For large amounts, waiting for about six blocks (roughly an hour) is the traditional comfort zone.&lt;/p&gt;

&lt;h3&gt;
  
  
  The "51 percent attack," in plain terms
&lt;/h3&gt;

&lt;p&gt;You may have heard that if someone controlled a majority of the network's mining power, they could attack Bitcoin. That is true, but it is worth being precise about what they could and could not do.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Could&lt;/strong&gt; do: with the majority of power, an attacker could out-build the honest network and rewrite &lt;em&gt;recent&lt;/em&gt; history, which would let them undo one of their own recent payments and effectively spend the same coins twice.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Could not&lt;/strong&gt; do, even with all that power: steal coins from an address they do not have the key for (they still cannot forge a signature), create coins out of thin air beyond the rules, or change the rules everyone else's nodes enforce. The referees would simply reject anything invalid.&lt;/p&gt;

&lt;p&gt;So even the scariest attack is limited, expensive, and easy to notice. It cannot quietly drain your wallet.&lt;/p&gt;

&lt;h2&gt;
  
  
  Upgrading a system with no boss
&lt;/h2&gt;

&lt;p&gt;If nobody is in charge, how does Bitcoin ever get updated? Carefully, and with the agreement of the people running nodes. There are two flavours of change:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;strong&gt;soft fork&lt;/strong&gt; tightens the rules in a way that older software still accepts. It is backwards compatible, so it can roll out smoothly. The 2017 upgrade known as &lt;strong&gt;SegWit&lt;/strong&gt; and the 2021 upgrade known as &lt;strong&gt;Taproot&lt;/strong&gt; were both soft forks. They made transactions more efficient and more private without forcing everyone to upgrade on the same day.&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;hard fork&lt;/strong&gt; changes the rules in a way older software will &lt;em&gt;reject&lt;/em&gt;. If some people upgrade and some do not, the network can split permanently into two separate coins. This is a much bigger deal and much rarer, because it needs near-universal agreement to avoid a messy divorce.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The short version: Bitcoin can improve, but only by broad agreement, and it strongly prefers changes that do not break anyone who has not upgraded yet.&lt;/p&gt;

&lt;h2&gt;
  
  
  Going faster: layers on top
&lt;/h2&gt;

&lt;p&gt;Every payment we have described so far gets written into the shared record, checked by every referee on Earth. That is wonderfully secure, but it is not built for buying a coffee. It is a bit like asking the whole world to witness and file paperwork for a $3 purchase.&lt;/p&gt;

&lt;p&gt;The common solution is to add a faster layer on top, the best known being the &lt;strong&gt;Lightning Network&lt;/strong&gt;. The idea in plain terms: two people (or a chain of people) open a private tab between them, make as many instant, nearly free payments back and forth as they like, and only write to the main record twice, once to open the tab and once to close it and settle the final total.&lt;/p&gt;

&lt;p&gt;It is the same instinct as running a bar tab instead of paying the bank for every single sip. The slow, heavily witnessed main chain becomes the final settlement layer, and the fast layer on top handles the everyday small stuff.&lt;/p&gt;

&lt;h2&gt;
  
  
  The machines and the electricity question
&lt;/h2&gt;

&lt;p&gt;Early on, people mined Bitcoin on ordinary computers. That did not last. Because mining is just the same fingerprint calculation repeated as fast as possible, hardware makers built chips that do &lt;em&gt;only&lt;/em&gt; that one calculation and nothing else, insanely fast. These are called &lt;strong&gt;ASICs&lt;/strong&gt;, and today serious mining happens in warehouses full of them.&lt;/p&gt;

&lt;p&gt;This is where the well-known energy debate comes from, and it is worth stating both sides fairly rather than picking one:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The concern:&lt;/strong&gt; all that guessing burns a genuinely large amount of electricity for a task that, from the outside, produces no physical product. Critics argue that is wasteful and adds to carbon emissions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The response:&lt;/strong&gt; supporters argue the electricity is the &lt;em&gt;point&lt;/em&gt;, since that real cost is exactly what makes cheating too expensive. They also point out that miners chase the cheapest power, which is often surplus, stranded, or renewable energy that would otherwise go unused, and that plenty of other industries use comparable amounts of energy with less scrutiny.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Both sides have real arguments. Where you land tends to depend on how much value you think a boss-free, tamper-resistant global record actually provides. This guide is not going to settle that for you, and honest people disagree.&lt;/p&gt;

&lt;h2&gt;
  
  
  A few myths worth clearing up
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;"Bitcoin is anonymous."&lt;/strong&gt; Not really. Every payment is public and permanent. Names are not attached directly, but the trail of coins can often be followed and linked to real identities. It is more like writing in a public ledger under a pen name than being invisible.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;"A bitcoin is a coin sitting in a file somewhere."&lt;/strong&gt; No. There is no coin object. There is only the record of which keys control which chunks of value. Owning Bitcoin means holding a key that can sign.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;"You have to buy a whole bitcoin."&lt;/strong&gt; No. Each coin divides into 100 million tiny units, so you can own and send a very small slice.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;"Miners approve or reject your payment based on who you are."&lt;/strong&gt; No. Miners mostly just take the highest fees. The rules are enforced by the referee nodes, not decided by miners' opinions of you.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;"If the price crashes, the coins vanish."&lt;/strong&gt; No. Price is what people will pay. The record of who owns what does not care about the price and keeps working exactly the same either way.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The whole life of a payment, start to finish
&lt;/h2&gt;

&lt;p&gt;Let us tie every layer together by following one payment from the moment you tap send to the moment it is permanent.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhsta37vnpv7b4b8dlcmn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhsta37vnpv7b4b8dlcmn.png" alt="btc-diagram-pay-cycle" width="799" height="264"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Look back at what each step used:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Step 1 is the &lt;strong&gt;UTXO&lt;/strong&gt; model (coins as cash): whole coins in, payment and change out.&lt;/li&gt;
&lt;li&gt;Step 2 is a &lt;strong&gt;digital signature&lt;/strong&gt;: the wax seal only you can press.&lt;/li&gt;
&lt;li&gt;Steps 3 and 4 are the &lt;strong&gt;nodes&lt;/strong&gt; doing their referee job.&lt;/li&gt;
&lt;li&gt;Step 5 is the &lt;strong&gt;mempool and fees&lt;/strong&gt;: the waiting room and the tip.&lt;/li&gt;
&lt;li&gt;Steps 6 and 7 are &lt;strong&gt;proof of work&lt;/strong&gt;: the guessing game.&lt;/li&gt;
&lt;li&gt;Step 8 is the &lt;strong&gt;chain and confirmations&lt;/strong&gt;: each new block locks the ones below it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is the entire machine. Fingerprints at the bottom. Signatures for ownership. Transactions built from signatures. Blocks built from transactions. A chain built from blocks. Agreement built from work. And an economy built on top of all of it.&lt;/p&gt;

&lt;p&gt;None of it needs a boss. It needs only a rule that everyone can check for themselves, and a game that makes honesty the cheapest option. That is the quiet, stubborn idea at the centre of Bitcoin.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next in this series
&lt;/h2&gt;

&lt;p&gt;We have covered &lt;em&gt;why&lt;/em&gt; mining works and &lt;em&gt;what&lt;/em&gt; the guessing game is. In the next post we will get our hands dirty and actually watch proof of work happen on a real machine, using a mining program called XMRig to make the loop visible and tangible.&lt;/p&gt;

&lt;p&gt;Small but important heads-up for later: XMRig cannot mine Bitcoin. Bitcoin mining today needs those special ASIC chips from the hardware section above, not a regular computer's processor. XMRig mines a different, processor-friendly coin. We are using it purely as a clear, hands-on window into the same guess-and-check idea you met here. See you there.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Reference figures (network size, coins mined) reflect roughly mid-2026 and drift over time. The protocol rules (ten-minute target, the halving schedule, the 21 million cap, the block layout) are fixed by the software. When in doubt about a live number, check a public block explorer.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://devarshi.dev/blog/how-the-bitcoin-network-actually-works" rel="noopener noreferrer"&gt;devarshi.dev&lt;/a&gt;, where all six demos are interactive.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>webdev</category>
      <category>cryptocurrency</category>
      <category>bitcoin</category>
    </item>
    <item>
      <title>5 Kyverno Policies to Secure Your Kubernetes Cluster</title>
      <dc:creator>Devarshi Shimpi</dc:creator>
      <pubDate>Sat, 11 Jul 2026 17:16:41 +0000</pubDate>
      <link>https://dev.to/devarshishimpi/5-kyverno-policies-to-secure-your-kubernetes-cluster-3hb9</link>
      <guid>https://dev.to/devarshishimpi/5-kyverno-policies-to-secure-your-kubernetes-cluster-3hb9</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;If you run workloads on Kubernetes, you already know how easy it is for small mistakes to slip into production. A missing resource limit here, a container running as root there, and suddenly your cluster has problems nobody planned for. This is exactly the kind of thing policy as code was built to fix, and &lt;strong&gt;&lt;a href="https://kyverno.io/" rel="noopener noreferrer"&gt;Kyverno&lt;/a&gt;&lt;/strong&gt; is one of the easiest tools to start with.&lt;/p&gt;

&lt;p&gt;Kyverno is a policy engine built specifically for Kubernetes. Instead of learning a new language to write rules, you write policies in plain YAML, the same format you already use for your deployments and services. That alone makes it a great starting point if you are new to Kubernetes security. Kyverno is also a CNCF Graduated project, which means it has passed a high bar for stability and community trust, and it is already used in production by companies like Spotify, LinkedIn, and Adidas.&lt;/p&gt;

&lt;p&gt;In this post, we will walk through five Kyverno policies. We will start simple and end with something a bit more advanced, verifying container image signatures. Each one comes with a small demo so you can see exactly what happens when a policy blocks something. By the end, you will have a solid feel for how Kyverno works and a few policies you can actually use.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fk9c6wlm2pbhm7sccudsl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fk9c6wlm2pbhm7sccudsl.png" alt="how-kyverno-works-diagram" width="800" height="407"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Before we get into the policies, here is a quick picture of how Kyverno fits into the request flow. Every time you or your CI pipeline sends something to Kubernetes, Kyverno gets a chance to check it first.&lt;/p&gt;

&lt;p&gt;A quick note before we start. Kyverno recently introduced a newer, CEL based way of writing policies, and the classic format used below (&lt;code&gt;ClusterPolicy&lt;/code&gt;) is now marked as deprecated in the official docs, though it is still fully supported and works today. It is still the easiest format to learn as a beginner, and this guide points you to the migration path at the end, once you are ready for it.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Require Resource Limits
&lt;/h2&gt;

&lt;p&gt;One of the most common problems in a shared cluster is a single pod using up all the CPU or memory on a node. This is often called the "noisy neighbor" problem, and it can slow down or crash other workloads running nearby.&lt;/p&gt;

&lt;p&gt;This policy makes sure every pod defines both requests and limits for CPU and memory before it is allowed to run. You can see the full syntax for this kind of rule in the &lt;a href="https://kyverno.io/docs/policy-types/cluster-policy/validate/" rel="noopener noreferrer"&gt;Kyverno validate rules docs&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;kyverno.io/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ClusterPolicy&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;require-requests-limits&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;background&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;validate-resources&lt;/span&gt;
      &lt;span class="na"&gt;match&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;any&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;kinds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;Pod&lt;/span&gt;
      &lt;span class="na"&gt;validate&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;failureAction&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Enforce&lt;/span&gt;
        &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CPU&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;and&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;memory&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;resource&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;requests&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;and&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;limits&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;are&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;required."&lt;/span&gt;
        &lt;span class="na"&gt;pattern&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                  &lt;span class="na"&gt;requests&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                    &lt;span class="na"&gt;memory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;?*"&lt;/span&gt;
                    &lt;span class="na"&gt;cpu&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;?*"&lt;/span&gt;
                  &lt;span class="na"&gt;limits&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                    &lt;span class="na"&gt;memory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;?*"&lt;/span&gt;
                    &lt;span class="na"&gt;cpu&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;?*"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once this is applied, any pod that does not set these values will simply be rejected. It is a small rule, but it prevents a lot of headaches later.&lt;/p&gt;

&lt;h3&gt;
  
  
  Demo: what happens if you skip resource limits
&lt;/h3&gt;

&lt;p&gt;Say you try to apply this pod, which has no requests or limits set.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Pod&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;no-limits-pod&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;app&lt;/span&gt;
      &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx:1.27&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; no-limits-pod.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Error from server: error when creating &lt;span class="s2"&gt;"no-limits-pod.yaml"&lt;/span&gt;: admission webhook &lt;span class="s2"&gt;"validate.kyverno.svc-fail"&lt;/span&gt; denied the request:

resource Pod/default/no-limits-pod was blocked due to the following policies

require-requests-limits:
  validate-resources: &lt;span class="s1"&gt;'CPU and memory resource requests and limits are required.
    Validation rule validate-resources failed at path /spec/containers/0/resources/'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now add the missing fields and try again.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Pod&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;with-limits-pod&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;app&lt;/span&gt;
      &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx:1.27&lt;/span&gt;
      &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;requests&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;memory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;128Mi"&lt;/span&gt;
          &lt;span class="na"&gt;cpu&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;100m"&lt;/span&gt;
        &lt;span class="na"&gt;limits&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;memory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;256Mi"&lt;/span&gt;
          &lt;span class="na"&gt;cpu&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;250m"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; with-limits-pod.yaml
&lt;span class="c"&gt;# pod/with-limits-pod created&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. The pod goes through as soon as it meets the policy.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Block the "latest" Image Tag
&lt;/h2&gt;

&lt;p&gt;It is tempting to just use &lt;code&gt;image: myapp:latest&lt;/code&gt; when you are getting started. The problem is that "latest" can point to a different image every time it is rebuilt. This makes deployments unpredictable and makes it very hard to know exactly what version of your app is actually running.&lt;/p&gt;

&lt;p&gt;This policy blocks any pod that uses the "latest" tag, or no tag at all.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;kyverno.io/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ClusterPolicy&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;disallow-latest-tag&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;background&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;require-image-tag&lt;/span&gt;
      &lt;span class="na"&gt;match&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;any&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;kinds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;Pod&lt;/span&gt;
      &lt;span class="na"&gt;validate&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;failureAction&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Enforce&lt;/span&gt;
        &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;An&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;image&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;tag&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;is&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;required."&lt;/span&gt;
        &lt;span class="na"&gt;pattern&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;*:*"&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;validate-image-tag&lt;/span&gt;
      &lt;span class="na"&gt;match&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;any&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;kinds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;Pod&lt;/span&gt;
      &lt;span class="na"&gt;validate&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;failureAction&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Enforce&lt;/span&gt;
        &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Using&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;a&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;mutable&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;image&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;tag&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;like&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;'latest'&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;is&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;not&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;allowed."&lt;/span&gt;
        &lt;span class="na"&gt;pattern&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;!*:latest"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This forces every team to use a proper version tag, which makes rollbacks and debugging much easier down the road. This exact example is adapted from the sample policy library that ships with the Kyverno project.&lt;/p&gt;

&lt;h3&gt;
  
  
  Demo: what happens if you use "latest"
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Pod&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;latest-tag-pod&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;app&lt;/span&gt;
      &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;myapp:latest&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; latest-tag-pod.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Error from server: error when creating &lt;span class="s2"&gt;"latest-tag-pod.yaml"&lt;/span&gt;: admission webhook &lt;span class="s2"&gt;"validate.kyverno.svc-fail"&lt;/span&gt; denied the request:

resource Pod/default/latest-tag-pod was blocked due to the following policies

disallow-latest-tag:
  validate-image-tag: &lt;span class="s1"&gt;'Using a mutable image tag like ''latest'' is not allowed.
    Validation rule validate-image-tag failed at path /spec/containers/0/image/'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Swap in a real version and the pod goes through without any trouble.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Pod&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;versioned-pod&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;app&lt;/span&gt;
      &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;myapp:1.4.2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; versioned-pod.yaml
&lt;span class="c"&gt;# pod/versioned-pod created&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  3. Require Labels
&lt;/h2&gt;

&lt;p&gt;Labels are how Kubernetes keeps things organized. They help you filter resources, connect them to monitoring tools, and figure out who owns what. Without labels, a growing cluster turns into a pile of unlabeled boxes nobody can identify.&lt;/p&gt;

&lt;p&gt;This policy requires every pod to have a specific label before it can be created. If you want a refresher on what labels are for, the Kubernetes labels documentation is a good place to start.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;kyverno.io/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ClusterPolicy&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;require-labels&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;check-for-labels&lt;/span&gt;
      &lt;span class="na"&gt;match&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;any&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;kinds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;Pod&lt;/span&gt;
      &lt;span class="na"&gt;validate&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;failureAction&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Enforce&lt;/span&gt;
        &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;The&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;label&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;'app.kubernetes.io/name'&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;is&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;required."&lt;/span&gt;
        &lt;span class="na"&gt;pattern&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;app.kubernetes.io/name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;?*"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can add more required labels over time, like team name or environment, once this basic rule feels comfortable.&lt;/p&gt;

&lt;h3&gt;
  
  
  Demo: what happens if a label is missing
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Pod&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;unlabeled-pod&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;app&lt;/span&gt;
      &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;myapp:1.4.2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; unlabeled-pod.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Error from server: error when creating &lt;span class="s2"&gt;"unlabeled-pod.yaml"&lt;/span&gt;: admission webhook &lt;span class="s2"&gt;"validate.kyverno.svc-fail"&lt;/span&gt; denied the request:

resource Pod/default/unlabeled-pod was blocked due to the following policies

require-labels:
  check-for-labels: &lt;span class="s1"&gt;'The label ''app.kubernetes.io/name'' is required.
    Validation rule check-for-labels failed at path /metadata/labels/'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add the label and the same pod is accepted right away.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Pod&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;labeled-pod&lt;/span&gt;
  &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;app.kubernetes.io/name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;myapp&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;app&lt;/span&gt;
      &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;myapp:1.4.2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; labeled-pod.yaml
&lt;span class="c"&gt;# pod/labeled-pod created&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  4. Restrict Privileged Containers
&lt;/h2&gt;

&lt;p&gt;A privileged container can do almost anything the host machine can do, including accessing other containers and system resources it should never touch. This is one of the biggest security risks in Kubernetes, and it is usually not something you want to allow unless there is a very specific reason for it.&lt;/p&gt;

&lt;p&gt;This policy blocks any pod from running in privileged mode. It is also one of the checks covered by the &lt;a href="https://kubernetes.io/docs/concepts/security/pod-security-standards/" rel="noopener noreferrer"&gt;Kubernetes Pod Security Standards&lt;/a&gt;, which Kyverno has its own dedicated guide for if you want to enforce the whole standard at once instead of writing individual rules.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;kyverno.io/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ClusterPolicy&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;disallow-privileged-containers&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;background&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;privileged-containers&lt;/span&gt;
      &lt;span class="na"&gt;match&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;any&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;kinds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;Pod&lt;/span&gt;
      &lt;span class="na"&gt;validate&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;failureAction&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Enforce&lt;/span&gt;
        &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Privileged&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;mode&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;is&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;not&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;allowed."&lt;/span&gt;
        &lt;span class="na"&gt;pattern&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;=(securityContext)&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                  &lt;span class="na"&gt;=(privileged)&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;false"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is one of the simplest changes you can make that meaningfully reduces your attack surface.&lt;/p&gt;

&lt;h3&gt;
  
  
  Demo: what happens if a pod asks for privileged mode
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Pod&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;privileged-pod&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;app&lt;/span&gt;
      &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;myapp:1.4.2&lt;/span&gt;
      &lt;span class="na"&gt;securityContext&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;privileged&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; privileged-pod.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Error from server: error when creating &lt;span class="s2"&gt;"privileged-pod.yaml"&lt;/span&gt;: admission webhook &lt;span class="s2"&gt;"validate.kyverno.svc-fail"&lt;/span&gt; denied the request:

resource Pod/default/privileged-pod was blocked due to the following policies

disallow-privileged-containers:
  privileged-containers: &lt;span class="s1"&gt;'Privileged mode is not allowed.
    Validation rule privileged-containers failed at path /spec/containers/0/securityContext/privileged/'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Remove the privileged flag, and the pod is created normally.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Pod&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;safe-pod&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;app&lt;/span&gt;
      &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;myapp:1.4.2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; safe-pod.yaml
&lt;span class="c"&gt;# pod/safe-pod created&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  5. Verify Container Image Signatures
&lt;/h2&gt;

&lt;p&gt;This last one is a step up in complexity, but it is worth learning because it solves a real problem, how do you know the image running in your cluster is actually the one your team built, and not something tampered with along the way?&lt;/p&gt;

&lt;p&gt;This is where image signing comes in. Tools like &lt;a href="https://github.com/sigstore/cosign" rel="noopener noreferrer"&gt;Cosign&lt;/a&gt;, part of the Sigstore project, let you cryptographically sign container images when you build them. Kyverno can then check that signature before allowing the image to run, and reject anything that is not signed or does not match. The full details of this feature are in the &lt;a href="https://kyverno.io/docs/policy-types/cluster-policy/verify-images/sigstore/" rel="noopener noreferrer"&gt;Kyverno Sigstore verification docs&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;kyverno.io/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ClusterPolicy&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;check-image-signature&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;background&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
  &lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;check-signature&lt;/span&gt;
      &lt;span class="na"&gt;match&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;any&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;kinds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;Pod&lt;/span&gt;
      &lt;span class="na"&gt;verifyImages&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;imageReferences&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ghcr.io/yourorg/*"&lt;/span&gt;
          &lt;span class="na"&gt;failureAction&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Enforce&lt;/span&gt;
          &lt;span class="na"&gt;attestors&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;entries&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;keys&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                    &lt;span class="na"&gt;publicKeys&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|-&lt;/span&gt;
                      &lt;span class="s"&gt;-----BEGIN PUBLIC KEY-----&lt;/span&gt;
                      &lt;span class="s"&gt;&amp;lt;your public key here&amp;gt;&lt;/span&gt;
                      &lt;span class="s"&gt;-----END PUBLIC KEY-----&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the same idea behind supply chain security, which has become a major topic in the industry after several high profile attacks where malicious code got smuggled into trusted software. Signing and verifying images is one of the clearest ways to close that gap, and it is a great skill to have even if you are still early in your Kubernetes journey.&lt;/p&gt;

&lt;h3&gt;
  
  
  Demo: what happens with an unsigned image
&lt;/h3&gt;

&lt;p&gt;First, you would sign your image once, when you build it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;cosign sign &lt;span class="nt"&gt;--key&lt;/span&gt; cosign.key ghcr.io/yourorg/myapp:1.4.2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now if someone tries to deploy an image that was never signed, Kyverno stops it. Note that image verification is handled through Kyverno's mutating webhook, so the error will mention "mutate" rather than "validate" even though the outcome is a rejection.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Pod&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;unsigned-image-pod&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;app&lt;/span&gt;
      &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ghcr.io/yourorg/myapp:1.0.0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; unsigned-image-pod.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Error from server: admission webhook &lt;span class="s2"&gt;"mutate.kyverno.svc"&lt;/span&gt; denied the request:

resource Pod/default/unsigned-image-pod was blocked due to the following policies

check-image-signature:
  check-signature: &lt;span class="s1"&gt;'image verification failed for ghcr.io/yourorg/myapp:1.0.0:
    signature not found'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Deploy the signed version instead, and it goes through.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Pod&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;signed-image-pod&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;app&lt;/span&gt;
      &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ghcr.io/yourorg/myapp:1.4.2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; signed-image-pod.yaml
&lt;span class="c"&gt;# pod/signed-image-pod created&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;None of these policies are complicated on their own, but together they cover a lot of ground: resource limits, image tagging, labeling, privilege restrictions, and image signing. If you are new to Kyverno, a good approach is to start with the first two policies, get comfortable applying them with &lt;code&gt;kubectl apply -f policy.yaml&lt;/code&gt;, watch how Kyverno reports violations, and then work your way down the list. You can try all of this without a real cluster using the Kyverno Playground.&lt;/p&gt;

&lt;p&gt;Kyverno is actively evolving too. As of version 1.18, the &lt;code&gt;ClusterPolicy&lt;/code&gt; style shown in this guide is marked deprecated in the docs in favor of newer CEL based policy types like &lt;code&gt;ValidatingPolicy&lt;/code&gt;. It still works fully today and remains the easiest format for a beginner to learn, but once you are comfortable, it is worth reading the &lt;a href="https://kyverno.io/docs/guides/migration-to-cel/" rel="noopener noreferrer"&gt;guide to migrating to CEL policies&lt;/a&gt; to see where the project is headed.&lt;/p&gt;

&lt;p&gt;If you want to go further, the official Kyverno documentation has a large library of sample policies you can copy and adapt for your own cluster, and the &lt;a href="https://github.com/kyverno/kyverno" rel="noopener noreferrer"&gt;Kyverno GitHub repository&lt;/a&gt; is where the project itself lives if you want to see what is being worked on next.&lt;/p&gt;

&lt;p&gt;Thank you for reading! If you found this blog post helpful, please consider sharing it with others who might benefit. Feel free to check out my other blog posts and visit my socials!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://devarshi.dev" rel="noopener noreferrer"&gt;Profile&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://linkedin.com/in/devarshi-shimpi" rel="noopener noreferrer"&gt;Linkedin&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://twitter.com/devarshishimpi" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://youtube.com/@devarshishimpi" rel="noopener noreferrer"&gt;Youtube&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Read more
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://devarshi.dev/blog/react-rsc-exploits-are-real-and-its-hurting-engineering-teams" rel="noopener noreferrer"&gt;React (RSC) Exploits Are Real and It's Hurting Engineering Teams&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://devarshi.dev/blog/managing-dependencies-and-security-with-npm" rel="noopener noreferrer"&gt;Managing Dependencies and Security in JavaScript with NPM&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://devarshi.dev/blog/deploy-minio-on-amazon-eks-self-host-s3-storage" rel="noopener noreferrer"&gt;Deploy MinIO on Amazon EKS: Self-Host S3 Storage&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>devops</category>
      <category>cloud</category>
      <category>security</category>
      <category>kubernetes</category>
    </item>
    <item>
      <title>I Wanted AI Code Review I Could Actually Own. So I Built Codra.</title>
      <dc:creator>Devarshi Shimpi</dc:creator>
      <pubDate>Tue, 23 Jun 2026 21:54:41 +0000</pubDate>
      <link>https://dev.to/devarshishimpi/i-wanted-ai-code-review-i-could-actually-own-so-i-built-codra-349n</link>
      <guid>https://dev.to/devarshishimpi/i-wanted-ai-code-review-i-could-actually-own-so-i-built-codra-349n</guid>
      <description>&lt;p&gt;I wanted AI code review I could actually own.&lt;/p&gt;

&lt;p&gt;Not access through a subscription or a black-box service with its own limits. The deployment, credentials, providers, and usage under my control.&lt;/p&gt;

&lt;p&gt;I kept hitting usage limits mid-week during deep building sessions. The models were capable. The workflow was useful. But access still depended on somebody else's weekly allowance, and centralized platforms can change whenever the company behind them decides to. Pricing, quotas, models, plan boundaries.&lt;/p&gt;

&lt;p&gt;A workflow that fits this month may sit behind another subscription next month. I could not find a reliable open-source option that gave me the ownership model I wanted. So I built one.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsi7wpdau2r5gvn9z8v7l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsi7wpdau2r5gvn9z8v7l.png" alt="codra-ai-code-review-dashboard" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That became &lt;strong&gt;&lt;a href="https://github.com/devarshishimpi/codra" rel="noopener noreferrer"&gt;Codra&lt;/a&gt;&lt;/strong&gt;: A self-hosted AI review engine built around bring-your-own models, your own data boundary, and no Codra-imposed usage ceiling.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Codra Is
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/devarshishimpi/codra" rel="noopener noreferrer"&gt;Codra&lt;/a&gt;&lt;/strong&gt; is an open-source, self-hosted AI code review engine for GitHub pull requests.&lt;/p&gt;

&lt;p&gt;It listens to pull request events, reviews changed files, posts inline findings, and provides a dashboard for jobs, repositories, model routing, history, usage, and failures.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwl3ivbnr40shry73llz3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwl3ivbnr40shry73llz3.png" alt="codra-github-inline-pr-comment" width="800" height="623"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It runs on Cloudflare Workers and uses:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cloudflare Queues for review jobs&lt;/li&gt;
&lt;li&gt;PostgreSQL through Hyperdrive for storage&lt;/li&gt;
&lt;li&gt;KV for sessions and cache&lt;/li&gt;
&lt;li&gt;A React dashboard for operations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The GitHub App, model credentials, database, and review history are yours. Provider keys are encrypted with AES-GCM using your deployment secret.&lt;/p&gt;

&lt;h3&gt;
  
  
  Bring Your Own Model, Bring Your Own Limits
&lt;/h3&gt;

&lt;p&gt;Changing providers does not require replacing your review history, configuration, or workflow.&lt;/p&gt;

&lt;p&gt;You configure the provider and model. Supported:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;OpenAI-compatible APIs&lt;/li&gt;
&lt;li&gt;OpenRouter&lt;/li&gt;
&lt;li&gt;Anthropic&lt;/li&gt;
&lt;li&gt;Google / Gemini&lt;/li&gt;
&lt;li&gt;Cloudflare Workers AI&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Why Self-Hosted Matters Here
&lt;/h3&gt;

&lt;p&gt;A large frontend repo and a tiny backend repo should not need the same review strategy.&lt;/p&gt;

&lt;p&gt;Each repository gets its own review settings. You tune triggers, skip generated files, ignore drafts, use mention-triggered reviews, configure labels, set file limits, and define custom rules.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Codra Actually Does
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://codra.run" rel="noopener noreferrer"&gt;Codra&lt;/a&gt;&lt;/strong&gt; is not just a prompt wrapped around a diff.&lt;/p&gt;

&lt;p&gt;It includes the pieces needed to operate AI review as a system:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Automatic and mention-triggered reviews&lt;/li&gt;
&lt;li&gt;Inline comments, summaries, and check runs&lt;/li&gt;
&lt;li&gt;Per-repository rules and skipped paths&lt;/li&gt;
&lt;li&gt;Provider fallbacks and size-based model routing&lt;/li&gt;
&lt;li&gt;Queue-backed, chunked processing with retries&lt;/li&gt;
&lt;li&gt;Dead letter queue recovery&lt;/li&gt;
&lt;li&gt;Job history, usage, repository, and model dashboards&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The boring pieces are the point.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0wq3qv1xpbxpaifxfr70.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0wq3qv1xpbxpaifxfr70.png" alt="codra-dashboard-job-issues" width="800" height="394"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Production software is mostly unhappy paths: provider outages, malformed output, large diffs, stale jobs, superseded reviews, and failed queue runs.&lt;/p&gt;

&lt;p&gt;Codra exists because those details matter.&lt;/p&gt;

&lt;h2&gt;
  
  
  Open Source Is Part of the Product
&lt;/h2&gt;

&lt;p&gt;Codra is AGPL-licensed because "own your AI review loop" only means something when the system is inspectable.&lt;/p&gt;

&lt;p&gt;Developers can inspect how jobs, diffs, prompts, fallbacks, comments, failures, and repository settings work. AI code review is still early, and those questions are better answered in the open:&lt;/p&gt;

&lt;p&gt;AI code review is still early. There are open questions everywhere:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Which models work best for different kinds of PRs?&lt;/li&gt;
&lt;li&gt;How strict should automated review be?&lt;/li&gt;
&lt;li&gt;How should local models and repository context fit in?&lt;/li&gt;
&lt;li&gt;How do we reduce noise without missing real bugs?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those questions are better answered in the open.&lt;/p&gt;

&lt;p&gt;Codra should improve through real codebases and community contributions to prompts, providers, docs, tests, and defaults.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Comes Next
&lt;/h2&gt;

&lt;p&gt;Still in beta, but the foundation is there. The next phase has three tracks.&lt;/p&gt;

&lt;p&gt;The next phase has three main tracks.&lt;/p&gt;

&lt;h3&gt;
  
  
  Deployment Targets
&lt;/h3&gt;

&lt;p&gt;Cloudflare Workers stays the primary target. These paths are for teams and&lt;br&gt;
individuals who want to run Codra on infrastructure they already control.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkjv7rdyzus39veu69k9k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkjv7rdyzus39veu69k9k.png" alt="discord-other-hosting-support-cloudflare-issue" width="800" height="509"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Vercel Functions adapter and other serverless runtime support&lt;/li&gt;
&lt;li&gt;Conventional server deployment via Node.js and PM2 for EC2 or any VM&lt;/li&gt;
&lt;li&gt;A Docker-first deployment path for platforms like Hetzner, DigitalOcean, and Coolify&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Stability and Developer Experience
&lt;/h3&gt;

&lt;p&gt;The review pipeline works. The focus now is making it hold up under real-world load&lt;br&gt;
and easier to get running without friction.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A more robust pipeline that handles large PRs reliably&lt;/li&gt;
&lt;li&gt;Cleaner installation, configuration, and debugging flows for first-time deployers&lt;/li&gt;
&lt;li&gt;Community-driven defaults and real-world review config examples&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Provider and Model Support
&lt;/h3&gt;

&lt;p&gt;Inference should not be locked to hosted APIs. Local and self-hosted models will&lt;br&gt;
become a first-class option alongside the existing provider list.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Improved support across existing providers&lt;/li&gt;
&lt;li&gt;Routes for local and self-hosted models, Ollama, LM Studio, and similar&lt;/li&gt;
&lt;li&gt;More language-aware review behavior per file type&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Own the Review Loop
&lt;/h2&gt;

&lt;p&gt;Providers, pricing, and quotas will keep changing. Your review engine should not have to change with them.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fv04aplai78la92f36wo8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fv04aplai78la92f36wo8.png" alt="codra-cta-banner" width="800" height="188"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/devarshishimpi/codra" rel="noopener noreferrer"&gt;Codra is open source on GitHub&lt;/a&gt;&lt;/strong&gt;. Try it, star it, break it, file issues, and help shape where it goes next.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Docs:&lt;/strong&gt; &lt;a href="https://codra.run/docs" rel="noopener noreferrer"&gt;https://codra.run/docs&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Read more
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://devarshi.dev/blog/codra-self-hosted-ai-code-review" rel="noopener noreferrer"&gt;I Wanted AI Code Review I Could Actually Own. So I Built Codra.&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://devarshi.dev/blog/self-hosting-your-own-cloud-storage-on-aws-using-nextcloud" rel="noopener noreferrer"&gt;Self-Hosting Your Own Cloud Storage on AWS using NextCloud&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://devarshi.dev/blog/how-to-create-a-new-nextjs-project-with-cf-workers-and-pages" rel="noopener noreferrer"&gt;How to create a new Next.js project with Cloudflare Pages and Workers&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>productivity</category>
      <category>opensource</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Voice Search in Chrome Extensions Is Harder Than It Looks</title>
      <dc:creator>Devarshi Shimpi</dc:creator>
      <pubDate>Mon, 16 Mar 2026 15:57:17 +0000</pubDate>
      <link>https://dev.to/devarshishimpi/voice-search-in-chrome-extensions-is-harder-than-it-looks-1pc</link>
      <guid>https://dev.to/devarshishimpi/voice-search-in-chrome-extensions-is-harder-than-it-looks-1pc</guid>
      <description>&lt;p&gt;I thought this was just an hour of work. It wasn't. Here's everything that went wrong and what actually worked in the end.&lt;/p&gt;

&lt;h2&gt;
  
  
  Starting with the obvious choice
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;window.SpeechRecognition&lt;/code&gt; is built into Chrome. No API keys, no server, completely free. It felt like the perfect starting point for adding voice search to my extension.&lt;br&gt;
After a day of testing, I dropped it.&lt;br&gt;
The mic permission popup was all over the place. Sometimes it would show up randomly mid-session. Sometimes it would just stop listening with no error, no warning, nothing. Results were different depending on the machine and OS. For a new tab extension that's meant to feel polished, that just wasn't good enough.&lt;br&gt;
I switched to Deepgram's Nova-3 model. It's fast, accurate, and sends results back in real time over WebSocket, so you don't have to wait for a full sentence before seeing any output.&lt;br&gt;
But now I needed a server, which kicked off a whole chain of problems.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fciwmrs2euzvxjb7hcvb7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fciwmrs2euzvxjb7hcvb7.png" alt="old-flow-browser-rec" width="800" height="335"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Why you can't call Deepgram directly from the extension
&lt;/h2&gt;

&lt;p&gt;Chrome extensions are easy to unpack and read. Any secret you put in the code is basically public. So keeping a Deepgram API key in the client was never going to work.&lt;br&gt;
I needed a server in the middle. My first thought was Railway. I've used it before and getting a Node server running there takes maybe five minutes. But I second guessed that pretty quickly. A server on Railway means it has to start up cold sometimes, it costs money every month, and the speed depends on where the server is located. Cloudflare Workers runs closer to the user since it runs at the edge, and the free plan is generous enough that a voice search API would run for free forever. Easy choice.&lt;br&gt;
The setup I had in mind: the extension sends raw audio to the Worker, the Worker passes it to Deepgram, and the text comes back the same way.&lt;/p&gt;

&lt;p&gt;Simple enough on paper. Actually building it was another story.&lt;/p&gt;
&lt;h3&gt;
  
  
  Hono looked like the right tool. It wasn't.
&lt;/h3&gt;

&lt;p&gt;I use Hono for pretty much everything on Cloudflare Workers. Clean, fast, easy routing. So I reached for it without thinking twice.&lt;br&gt;
Set up a &lt;code&gt;/transcribe&lt;/code&gt; route, handled the WebSocket upgrade, deployed it. The Worker just froze. No error from my code, just Cloudflare eventually killing the request with a 500 and a message saying the Worker "would never generate a response."&lt;br&gt;
Took me a while to figure out what was going on. Cloudflare needs a special &lt;code&gt;webSocket&lt;/code&gt; property on the &lt;code&gt;Response&lt;/code&gt; object to finish a WebSocket connection. That's how it knows which socket to hand back to the client.&lt;br&gt;
Hono wraps the built-in &lt;code&gt;Response&lt;/code&gt; to add its own features. Somewhere in that process, the &lt;code&gt;webSocket&lt;/code&gt; property gets lost. Cloudflare gets a response with no socket, doesn't know what to do, and kills the request.&lt;br&gt;
The fix was to skip Hono for this route and just write it raw:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pathname&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/transcribe&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pair&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;WebSocketPair&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;pair&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;accept&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;101&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;webSocket&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;101 Switching Protocols. Finally.&lt;/p&gt;

&lt;h3&gt;
  
  
  The timing bug I should've seen coming
&lt;/h3&gt;

&lt;p&gt;Audio was flowing but the Worker kept logging &lt;code&gt;Deepgram not ready&lt;/code&gt;. Pretty obvious in hindsight.&lt;br&gt;
The moment the client gets a 101 back, it starts sending audio right away. But the Worker had only just started its own connection to Deepgram. That connection wasn't open yet. So audio was showing up before Deepgram was ready to take it.&lt;br&gt;
My first fix was to wait for the Deepgram connection to open before sending the 101 back:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;deepgramSocket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onopen&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;101&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;webSocket&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This worked. But it quietly created a new problem I didn't notice until later.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cloudflare's 10ms CPU limit is real
&lt;/h3&gt;

&lt;p&gt;The free plan on Cloudflare Workers gives you 10ms of CPU time per request. Not total time, just active processing time. Waiting on a network call doesn't count, so in theory waiting for Deepgram to connect should be fine.&lt;br&gt;
In practice, keeping the request open through the whole connection process added enough overhead that CPU usage was sitting at 40 to 90ms. Cloudflare was going to start dropping requests.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6wg2ndg5sa0t1ec2s0vr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6wg2ndg5sa0t1ec2s0vr.png" alt="cf-workers-cpu-spike" width="800" height="251"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The fix was to stop waiting. Return the 101 right away, let Deepgram connect in the background, and just hold any audio that comes in too early:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;deepgramReady&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pendingChunks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

&lt;span class="nx"&gt;deepgramSocket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;open&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;deepgramReady&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;chunk&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;pendingChunks&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;deepgramSocket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;pendingChunks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;message&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;deepgramReady&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;pendingChunks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;deepgramSocket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;101&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;webSocket&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;CPU usage dropped to around 2ms. Deepgram still connects, it just doesn't hold everything else up while it does.&lt;/p&gt;

&lt;h3&gt;
  
  
  AudioWorklet and Manifest V3 don't work together
&lt;/h3&gt;

&lt;p&gt;This was the most painful part of the whole thing.&lt;br&gt;
MV3 extensions only let you run scripts that are part of your own extension package. No inline scripts, no scripts from other websites, nothing generated at runtime. &lt;code&gt;AudioWorkletNode&lt;/code&gt; is the standard modern way to process audio in a background thread, and it needs you to load a separate file using &lt;code&gt;addModule(url)&lt;/code&gt;. You can already see where this is going.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjlqiuid90nopwls495e8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjlqiuid90nopwls495e8.png" alt="devtools-error" width="800" height="396"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;First try was using Vite's &lt;code&gt;?url&lt;/code&gt; import:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;processorUrl&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./processor.js?url&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// Output: data:text/javascript;base64,...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Blocked. Makes sense, data URIs aren't allowed.&lt;br&gt;
Second try was putting the file in the &lt;code&gt;public/&lt;/code&gt; folder and loading it with &lt;code&gt;chrome.runtime.getURL&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;chrome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;runtime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getURL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;audio/pcm-processor.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;audioWorklet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addModule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This really should have worked. A &lt;code&gt;chrome-extension://&lt;/code&gt; URL is from your own extension, so the policy should allow it. I was pretty sure this was the answer.&lt;br&gt;
It failed with &lt;code&gt;AbortError: Unable to load worklet module&lt;/code&gt;. No extra info, no useful error message.&lt;br&gt;
After going through Chromium bug reports, turns out this is a known broken behaviour in how Chrome handles extension files in Worklet threads. There are open issues, no fix in sight.&lt;/p&gt;
&lt;h3&gt;
  
  
  The old deprecated API that actually works
&lt;/h3&gt;

&lt;p&gt;I gave up on &lt;code&gt;AudioWorklet&lt;/code&gt; and used &lt;code&gt;ScriptProcessorNode&lt;/code&gt; instead. It's been marked as deprecated for years but it runs directly in the page using a callback, so there's no extra file to load and no policy issues. For converting Float32 audio to Int16, the performance hit is basically nothing.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;processor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;audioCtx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createScriptProcessor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4096&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;processor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onaudioprocess&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;inputBuffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getChannelData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;int16&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;float32ToInt16&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;ws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;int16&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Works every time without fail. The modern recommended way broke. The old way just works. That's extension development.&lt;/p&gt;

&lt;h2&gt;
  
  
  A few smaller things worth knowing
&lt;/h2&gt;

&lt;p&gt;Cleaning up properly is harder than it sounds. Web Audio can leak memory if you don't clean up after yourself. When voice search stops, you need to close the WebSocket, disconnect the &lt;code&gt;ScriptProcessor&lt;/code&gt;, stop all microphone tracks (this turns off the mic light in the browser toolbar), and close the &lt;code&gt;AudioContext&lt;/code&gt;. Skip any one of these and you'll have audio processes running in the background that you can't see.&lt;br&gt;
Silence detection matters a lot. Without it, the mic just stays on forever. Every time Deepgram sends back a transcript or a &lt;code&gt;speech_started&lt;/code&gt; event, reset a 2.5 second timer. When the timer runs out, stop listening. It makes the whole thing feel much less annoying to use.&lt;br&gt;
Add the Worker URL to &lt;code&gt;host_permissions&lt;/code&gt; in your manifest. You need to allow &lt;code&gt;wss://your-worker.workers.dev/*&lt;/code&gt; or the browser blocks the connection before it even gets to any policy check.&lt;/p&gt;

&lt;h2&gt;
  
  
  What actually changed
&lt;/h2&gt;

&lt;p&gt;Before all of this, the extension was using &lt;code&gt;window.SpeechRecognition&lt;/code&gt; with around 500ms before it even started listening, results that changed between machines, and random failures. After the rewrite, it connects in around 100ms, the Worker uses 1 to 3ms of CPU per request, and it works the same way every time.&lt;br&gt;
Every single problem in this build came from the same place: the standard tools weren't built for this environment. Hono breaks WebSockets on Cloudflare. &lt;code&gt;AudioWorklet&lt;/code&gt; breaks in MV3 extensions. Once you understand why something fails, the fix is usually obvious. The hard part is getting there.&lt;/p&gt;

&lt;h2&gt;
  
  
  Happy Coding!!!
&lt;/h2&gt;

&lt;p&gt;Thank you for reading! If you found this blog post helpful, please consider sharing it with others who might benefit. Feel free to check out my other blog posts and visit my socials!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://devarshi.dev" rel="noopener noreferrer"&gt;Profile&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://linkedin.com/in/devarshi-shimpi" rel="noopener noreferrer"&gt;Linkedin&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://twitter.com/devarshishimpi" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://youtube.com/@devarshishimpi" rel="noopener noreferrer"&gt;Youtube&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://devarshishimpi.hashnode.dev" rel="noopener noreferrer"&gt;Hashnode&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/devarshishimpi"&gt;DEV&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Read more
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://devarshi.dev/blog/self-hosting-your-own-cloud-storage-on-aws-using-nextcloud" rel="noopener noreferrer"&gt;Self-Hosting Your Own Cloud Storage on AWS using NextCloud&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://devarshi.dev/blog/creating-your-first-droplet-digitalocean-tutorials" rel="noopener noreferrer"&gt;Creating Your First Droplet - DigitalOcean Tutorials&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://devarshi.dev/blog/complete-markdown-tutorial-for-beginners" rel="noopener noreferrer"&gt;Complete Markdown Tutorial for Beginners&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>beginners</category>
      <category>react</category>
    </item>
    <item>
      <title>React (RSC) Exploits Are Real and It's Hurting Engineering Teams</title>
      <dc:creator>Devarshi Shimpi</dc:creator>
      <pubDate>Thu, 08 Jan 2026 16:11:01 +0000</pubDate>
      <link>https://dev.to/devarshishimpi/react-rsc-exploits-are-real-and-its-hurting-engineering-teams-32mi</link>
      <guid>https://dev.to/devarshishimpi/react-rsc-exploits-are-real-and-its-hurting-engineering-teams-32mi</guid>
      <description>&lt;p&gt;If you work in web engineering, the transition into 2026 hasn’t been defined by new features, but by a single, terrifying realization: &lt;strong&gt;The boundary between your client and server is broken.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In December, "React2Shell" (&lt;a href="https://react.dev/blog/2025/12/03/critical-security-vulnerability-in-react-server-components" rel="noopener noreferrer"&gt;CVE-2025-55182&lt;/a&gt;) exposed a critical RCE in React Server Components. Barely after teams patched, two more high-severity exploits emerged in the same subsystem. This is the definitive technical post-mortem.&lt;/p&gt;

&lt;h2&gt;
  
  
  The "React2Shell" RCE (CVE-2025-55182)
&lt;/h2&gt;

&lt;p&gt;To understand why this vulnerability (CVSS 10.0) was so devastating, we first have to understand the machinery it destroyed, the &lt;strong&gt;React Flight Protocol&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;For years, the mental model for React developers was simple: React renders UI in the browser, and an API (REST/GraphQL) fetches data. The server and client were distinct worlds, separated by a clear network boundary.&lt;/p&gt;

&lt;p&gt;React Server Components (RSC) erased that boundary.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fctjey82m25yq03poe1hf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fctjey82m25yq03poe1hf.png" alt="react-flight-protocol" width="800" height="311"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the RSC world, the server doesn't send HTML to the client. It streams a proprietary serialization format called "Flight." This stream contains descriptions of UI components, serialized data, and crucially &lt;strong&gt;Promises&lt;/strong&gt; that resolve to data.&lt;/p&gt;

&lt;h3&gt;
  
  
  How React2Shell Works
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://react.dev/blog/2025/12/03/critical-security-vulnerability-in-react-server-components" rel="noopener noreferrer"&gt;React2Shell&lt;/a&gt; was not standard SQL injection or XSS. It was logic abuse targeting a deserializer. Attackers crafted HTTP POST requests with specific "Flight" payloads. These binary-like streams mimicked valid component trees while hiding a "Thenable" Object, or fake Promise.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Injection:&lt;/strong&gt; Attacker sends serialized object containing malicious &lt;code&gt;__proto__&lt;/code&gt; keys.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deserialization:&lt;/strong&gt; Server parses stream and tries resolving fake Promise.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prototype Pollution:&lt;/strong&gt; Lack of sanitization lets &lt;code&gt;__proto__&lt;/code&gt; overwrite &lt;code&gt;Object.prototype&lt;/code&gt; on running Node.js process.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gadget Chain:&lt;/strong&gt; Modified prototype triggers internal calls targeting &lt;code&gt;Function&lt;/code&gt; constructor.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Accessing &lt;code&gt;Function&lt;/code&gt; constructor means game over. Arbitrary strings like &lt;code&gt;require('child_process').exec(...)&lt;/code&gt; compile and execute immediately.&lt;/p&gt;

&lt;h3&gt;
  
  
  How Next.js was Affected
&lt;/h3&gt;

&lt;p&gt;Because &lt;a href="https://nextjs.org/blog/CVE-2025-66478" rel="noopener noreferrer"&gt;Next.js App Router&lt;/a&gt; enables RSC by default, &lt;strong&gt;every&lt;/strong&gt; Next.js application (versions 15.x, 16.x, and canary builds) was vulnerable out of the box. You didn't need to write a buggy Server Action to be hacked. You didn't even need to use Server Components explicitly. If your application booted up &lt;code&gt;react-server-dom-webpack&lt;/code&gt;, it was listening for these Flight payloads.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foblobv06ek862kwr6ejt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foblobv06ek862kwr6ejt.png" alt="client-server-components" width="800" height="335"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Credits: Next.js for the image&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  The DevOps Nightmare
&lt;/h3&gt;

&lt;p&gt;For DevOps engineers, "React2Shell" was a worst-case scenario.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;WAF Blindness:&lt;/strong&gt; Standard Web Application Firewalls (WAFs) are trained to look for SQL injection (&lt;code&gt;' OR 1=1&lt;/code&gt;) or script tags (&lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt;). They were &lt;em&gt;not&lt;/em&gt; trained to inspect the proprietary text format of React Flight. The malicious payloads passed right through Cloudflare and AWS WAF rules initially.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Supply Chain Hell:&lt;/strong&gt; The vulnerability wasn't in user code; it was deep in &lt;code&gt;node_modules&lt;/code&gt;. You couldn't just "fix the code." You had to wait for Vercel and the React team to release patched binaries, then rebuild and redeploy every single microservice.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Aftershocks (DoS and Leaks)
&lt;/h2&gt;

&lt;p&gt;Just as engineering teams were recovering from the RCE panic, the security community found more cracks in the foundation. On December 11, 2025, the &lt;a href="https://react.dev/blog/2025/12/11/denial-of-service-and-source-code-exposure-in-react-server-components" rel="noopener noreferrer"&gt;React team issued a new advisory&lt;/a&gt; for &lt;strong&gt;three&lt;/strong&gt; additional CVEs.&lt;/p&gt;

&lt;p&gt;While these aren't RCEs, they are operationally devastating and highlight the fragility of the current serialization implementation.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Denial of Service (CVE-2025-55184)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Severity: High (7.5/10)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This vulnerability allows attackers to "freeze" infrastructure with a single request.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mechanism:&lt;/strong&gt;&lt;br&gt;
Flight protocol allows data chunks to reference other chunks. Researchers discovered they could create &lt;strong&gt;circular dependencies&lt;/strong&gt; in payloads.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Chunk A references Chunk B.&lt;/li&gt;
&lt;li&gt;Chunk B references Chunk A.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When the React deserializer attempts to resolve this structure, it enters an &lt;strong&gt;infinite synchronous loop&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxztsnmc8k1n6pu7fugkg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxztsnmc8k1n6pu7fugkg.png" alt="nodejs-server-event-loop" width="800" height="414"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Impact on Infrastructure:&lt;/strong&gt;&lt;br&gt;
Because Node.js is single-threaded, this infinite loop blocks the entire Event Loop.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;CPU Spike:&lt;/strong&gt; The CPU immediately jumps to 100%.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Request Drops:&lt;/strong&gt; The server stops responding to &lt;em&gt;all&lt;/em&gt; other users. Health checks fail.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cluster Destabilization:&lt;/strong&gt; In Kubernetes, the liveness probe might fail, causing the pod to restart. But if the attacker sends a stream of these requests, they can put the entire cluster into a "CrashLoopBackOff" state, effectively taking down the application.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;em&gt;Note: The initial fix for this was incomplete, leading to CVE-2025-67779 (the "fix for the fix"), which forced DevOps teams to patch their systems a third time in two weeks.&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  2. Source Code Leak (CVE-2025-55183)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Severity: Medium (5.3/10)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Arguably most embarrassing vulnerability for React ecosystem. It allows attackers to trick servers into sending source code back to clients.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mechanism:&lt;/strong&gt;&lt;br&gt;
Exploit relies on JavaScript string coercion. If Server Action returns object implicitly calling &lt;code&gt;.toString()&lt;/code&gt; on a function within server runtime, default V8 behavior returns function &lt;strong&gt;source code&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"Secrets" Risk:&lt;/strong&gt;&lt;br&gt;
Developers often assume code inside &lt;code&gt;use server&lt;/code&gt; files is private. They might write:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// DO NOT DO THIS&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;STRIPE_KEY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sk_live_12345&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;purchase&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Under normal circumstances, &lt;code&gt;STRIPE_KEY&lt;/code&gt; stays on the server. But with CVE-2025-55183, an attacker can manipulate the serialization of the return value to dump the scope of the function, potentially revealing hardcoded API keys, internal comments, and database schema details.&lt;/p&gt;

&lt;h3&gt;
  
  
  Immediate Action Plan
&lt;/h3&gt;

&lt;p&gt;If you are running &lt;strong&gt;Next.js App Router (v13.3+)&lt;/strong&gt; or &lt;strong&gt;React 19&lt;/strong&gt;, you must execute the following plan immediately:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The "Immediate" Update:&lt;/strong&gt;
Do not trust semantic versioning ranges. Pin your versions to the releases that explicitly fix &lt;em&gt;all&lt;/em&gt; discussed CVEs.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;react@19.0.2 react-dom@19.0.2 next@15.1.3
&lt;span class="c"&gt;# Verify deeply nested dependencies&lt;/span&gt;
npm list react-server-dom-webpack

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Ensure &lt;code&gt;react-server-dom-webpack&lt;/code&gt; is at least version 19.0.2.&lt;/em&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Implement Rate Limiting:&lt;/strong&gt;&lt;br&gt;
The DoS exploit is cheap for attackers. You must implement aggressive rate limiting on all routes, especially &lt;code&gt;POST&lt;/code&gt; requests, at the infrastructure level (Nginx, Cloudflare, AWS WAF).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Audit for Secrets:&lt;/strong&gt;&lt;br&gt;
Assume your server code &lt;em&gt;can&lt;/em&gt; be leaked. Run a scan of your &lt;code&gt;app/&lt;/code&gt; directory. If you find a single API key, hardcoded password, or internal IP address in a &lt;code&gt;.ts&lt;/code&gt; or &lt;code&gt;.js&lt;/code&gt; file, move it to an environment variable immediately.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;React Server Components are a powerful evolution of the web, but "React2Shell" has proven that this power comes with a terrifying new attack surface. The Flight protocol is complex, and as we have seen, complexity is the enemy of security.&lt;/p&gt;

&lt;p&gt;Thank you for reading! If you found this blog post helpful, please consider sharing it with others who might benefit. Feel free to check out my other blog posts and visit my socials!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://devarshi.dev" rel="noopener noreferrer"&gt;Profile&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://linkedin.com/in/devarshi-shimpi" rel="noopener noreferrer"&gt;Linkedin&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://twitter.com/devarshishimpi" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://youtube.com/@devarshishimpi" rel="noopener noreferrer"&gt;Youtube&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Read more
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://devarshi.dev/blog/managing-dependencies-and-security-with-npm" rel="noopener noreferrer"&gt;Managing Dependencies and Security in JavaScript with NPM&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://devarshi.dev/blog/google-ends-its-url-shortener-and-breaks-urls" rel="noopener noreferrer"&gt;The End of Google URL Shortener and breaks 4.6 BILLION URLs!&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://devarshi.dev/blog/managing-metadata-in-nextjs-for-seo" rel="noopener noreferrer"&gt;Managing Metadata in Next.js for Enhanced SEO and User Experience&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>webdev</category>
      <category>react</category>
      <category>nextjs</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Dynamic Styling with calc() in TailwindCSS</title>
      <dc:creator>Devarshi Shimpi</dc:creator>
      <pubDate>Tue, 14 Oct 2025 22:01:56 +0000</pubDate>
      <link>https://dev.to/devarshishimpi/dynamic-styling-with-calc-in-tailwindcss-5pp</link>
      <guid>https://dev.to/devarshishimpi/dynamic-styling-with-calc-in-tailwindcss-5pp</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;TailwindCSS is fantastic, right? It lets us build beautiful, custom designs incredibly fast with its utility-first approach. We grab classes like &lt;code&gt;p-4&lt;/code&gt;, &lt;code&gt;flex&lt;/code&gt;, &lt;code&gt;w-1/2&lt;/code&gt;, and &lt;em&gt;boom&lt;/em&gt; – things start taking shape. But sometimes... sometimes you hit a wall. You need a layout element to be &lt;em&gt;exactly&lt;/em&gt; 100% wide, &lt;em&gt;minus&lt;/em&gt; the fixed width of a sidebar. Or maybe you want padding that dynamically adjusts based on the viewport &lt;em&gt;plus&lt;/em&gt; a base value.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwi6mpcqt69sz0mhvc5ab.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwi6mpcqt69sz0mhvc5ab.png" alt="tailwindcss-logo" width="644" height="210"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this deep dive, we'll explore how &lt;code&gt;calc()&lt;/code&gt; works, why it's such a powerful partner for Tailwind, and how you can leverage it to build more sophisticated, responsive, and pixel-perfect interfaces without ever leaving your HTML (mostly!).&lt;/p&gt;

&lt;h2&gt;
  
  
  First Things First: What Exactly &lt;em&gt;is&lt;/em&gt; &lt;code&gt;calc()&lt;/code&gt;?
&lt;/h2&gt;

&lt;p&gt;Before we plug it into Tailwind, let's quickly refresh ourselves on what &lt;code&gt;calc()&lt;/code&gt; actually does in plain old CSS. At its core, &lt;code&gt;calc()&lt;/code&gt; is a native CSS function that lets you perform calculations right inside your property values. Think of it like a mini-calculator for your stylesheets.&lt;/p&gt;

&lt;p&gt;It supports the basic arithmetic operations you'd expect:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Addition (&lt;code&gt;+&lt;/code&gt;)&lt;/strong&gt;: &lt;code&gt;calc(2rem + 10px)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Subtraction (&lt;code&gt;-&lt;/code&gt;)&lt;/strong&gt;: &lt;code&gt;calc(100% - 50px)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multiplication (&lt;code&gt;*&lt;/code&gt;)&lt;/strong&gt;: &lt;code&gt;calc(1.5 * 1rem)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Division (&lt;code&gt;/&lt;/code&gt;)&lt;/strong&gt;: &lt;code&gt;calc(100vw / 3)&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpk4ojuvtv3rx9kpgxhhx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpk4ojuvtv3rx9kpgxhhx.png" alt="width-calc-eg" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This ability to blend relative and absolute units is something static values just can't replicate easily.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/* Full width minus a fixed sidebar */&lt;/span&gt;
&lt;span class="nc"&gt;.main-content&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;calc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;100%&lt;/span&gt; &lt;span class="n"&gt;-&lt;/span&gt; &lt;span class="m"&gt;250px&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;/* Dynamic padding based on viewport height */&lt;/span&gt;
&lt;span class="nc"&gt;.hero-section&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;padding-top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;calc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;5vh&lt;/span&gt; &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="m"&gt;2rem&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Why Bring &lt;code&gt;calc()&lt;/code&gt; into the Tailwind World?
&lt;/h2&gt;

&lt;p&gt;"Okay," you might be thinking, "Tailwind has spacing, sizing, fractions... why complicate things?"&lt;/p&gt;

&lt;p&gt;That's a fair question! Tailwind covers a &lt;em&gt;huge&lt;/em&gt; range of common styling needs with its utilities. But &lt;code&gt;calc()&lt;/code&gt; fills the gaps where:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;You Need Mixed Units:&lt;/strong&gt; Tailwind's &lt;code&gt;w-1/2&lt;/code&gt; is 50%, &lt;code&gt;w-64&lt;/code&gt; is 16rem. But what about &lt;code&gt;calc(50% - 1rem)&lt;/code&gt;? Tailwind doesn't have a pre-built utility for every possible mathematical combination.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;You Need Precise Dynamic Relationships:&lt;/strong&gt; Calculating an element's size based on the viewport &lt;em&gt;minus&lt;/em&gt; a fixed header/footer height is a prime use case.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;You Want Fluidity Beyond Standard Fractions:&lt;/strong&gt; Maybe a three-column layout needs precise gutters subtracted from fractional widths.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;You're Dealing with External Constraints:&lt;/strong&gt; Sometimes, an element's size needs to account for something outside its direct parent (like the viewport height).&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Using Arbitrary Values &lt;code&gt;[]&lt;/code&gt; with &lt;code&gt;calc()&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;The framework embraced the idea that sometimes, you just need to write a specific CSS value directly. This is done using square brackets &lt;code&gt;[]&lt;/code&gt;. And guess what? You can put a &lt;code&gt;calc()&lt;/code&gt; expression right inside!&lt;/p&gt;

&lt;p&gt;The syntax looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"property-[calc(expression)]"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;...&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's see it in action:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Creating a width that's full-width minus some padding:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"w-[calc(100%-2rem)] bg-blue-100 p-4"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  My width adapts, always leaving 1rem of space on each side (total 2rem).
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Setting a height relative to the viewport minus a header:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;header&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"h-16 bg-gray-800 text-white"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;...&lt;span class="nt"&gt;&amp;lt;/header&amp;gt;&lt;/span&gt; &lt;span class="nt"&gt;&amp;lt;main&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"h-[calc(100vh-4rem)] bg-gray-100 overflow-y-auto"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  This content area fills the remaining vertical space perfectly.
&lt;span class="nt"&gt;&amp;lt;/main&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Applying dynamic margins or padding:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"mt-[calc(2rem+5vh)] bg-green-100 p-3"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  My top margin grows slightly as the viewport height increases.
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's that straightforward! You use the Tailwind property prefix (&lt;code&gt;w-&lt;/code&gt;, &lt;code&gt;h-&lt;/code&gt;, &lt;code&gt;mt-&lt;/code&gt;, &lt;code&gt;p-&lt;/code&gt;, etc.) followed by your &lt;code&gt;calc()&lt;/code&gt; expression wrapped in square brackets.&lt;/p&gt;

&lt;h2&gt;
  
  
  Let's Get Practical: Real-World &lt;code&gt;calc()&lt;/code&gt; Scenarios in Tailwind
&lt;/h2&gt;

&lt;p&gt;Theory is nice, but let's see where &lt;code&gt;calc()&lt;/code&gt; truly shines in everyday development with Tailwind.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example 1: The Classic Sidebar + Content Layout
&lt;/h3&gt;

&lt;p&gt;This is a bread-and-butter layout. You have a fixed-width sidebar and want the main content area to take up the &lt;em&gt;rest&lt;/em&gt; of the available space.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"flex min-h-screen"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;aside&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"w-64 bg-gray-100 p-4 flex-shrink-0"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; Sidebar Content
  &lt;span class="nt"&gt;&amp;lt;/aside&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;main&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"w-[calc(100%-16rem)] bg-white p-6 flex-grow"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    Main Application Content Here...
    It perfectly fills the space next to the sidebar.
  &lt;span class="nt"&gt;&amp;lt;/main&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, &lt;code&gt;w-[calc(100%-16rem)]&lt;/code&gt; explicitly tells the main content area to calculate its width based on the full container width minus the sidebar's known width. &lt;code&gt;flex-shrink-0&lt;/code&gt; on the sidebar prevents it from shrinking if space gets tight, and &lt;code&gt;flex-grow&lt;/code&gt; on main allows it to expand (though the &lt;code&gt;calc()&lt;/code&gt; width often makes this redundant, it's good practice in flex layouts).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbckil0itembwcm8pecqg.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbckil0itembwcm8pecqg.gif" alt="sidebar-content-layout" width="400" height="196"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Example 2: Making Room for Fixed Headers/Footers
&lt;/h3&gt;

&lt;p&gt;Sticky headers are common, but they can overlap content or require awkward padding. &lt;code&gt;calc()&lt;/code&gt; makes managing the space elegant.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;body&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"flex flex-col min-h-screen"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;header&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"h-16 bg-indigo-600 text-white p-4 sticky top-0 z-10"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; Fixed Header
  &lt;span class="nt"&gt;&amp;lt;/header&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;main&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"flex-grow p-6 bg-gray-50"&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"padding-top: calc(4rem + 1.5rem);"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    Content Section
  &lt;span class="nt"&gt;&amp;lt;/main&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;section&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"h-[calc(100vh-4rem)] bg-gray-100 p-6 overflow-y-auto"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
     This section takes exactly the remaining viewport height below the header.
  &lt;span class="nt"&gt;&amp;lt;/section&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;footer&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"h-12 bg-gray-700 text-white p-3 mt-auto"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; Footer
  &lt;span class="nt"&gt;&amp;lt;/footer&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the &lt;code&gt;section&lt;/code&gt; example, &lt;code&gt;h-[calc(100vh-4rem)]&lt;/code&gt; ensures that specific part of the page uses precisely the vertical space available below the 4rem header. No guesswork needed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example 3: Fluid Typography That Breathes
&lt;/h3&gt;

&lt;p&gt;Want your text headings to scale smoothly with the viewport width, not just jump between breakpoints? &lt;code&gt;calc()&lt;/code&gt; combined with viewport units (&lt;code&gt;vw&lt;/code&gt;) is your friend.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;h1&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"text-[calc(1.5rem+2vw)] font-bold text-gray-900 leading-tight"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  This Headline Scales Fluidly
&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"text-[calc(1rem+0.5vw)] text-gray-700"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  Even paragraph text can subtly adapt to the screen size, improving readability across devices.
&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, the font size has a base value (&lt;code&gt;1.5rem&lt;/code&gt; or &lt;code&gt;1rem&lt;/code&gt;) and adds a small percentage of the viewport width (&lt;code&gt;2vw&lt;/code&gt; or &lt;code&gt;0.5vw&lt;/code&gt;). This creates a much smoother scaling effect than relying solely on &lt;code&gt;text-lg&lt;/code&gt;, &lt;code&gt;text-xl&lt;/code&gt;, etc., at different breakpoints.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example 4: Precise Centering and Positioning
&lt;/h3&gt;

&lt;p&gt;Sometimes &lt;code&gt;flex&lt;/code&gt; or &lt;code&gt;grid&lt;/code&gt; centering isn't quite right, especially for absolutely positioned elements or overlays where you need an offset.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"relative h-screen bg-gray-200"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"absolute top-[calc(50%-50px)] left-[50%] -translate-x-1/2 -translate-y-1/2 bg-white p-8 rounded shadow-lg"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    I'm centered horizontally, but slightly offset vertically!
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, &lt;code&gt;top-[calc(50%-50px)]&lt;/code&gt; positions the element's top edge 50 pixels &lt;em&gt;above&lt;/em&gt; the halfway point of the container, while the &lt;code&gt;transform&lt;/code&gt; utilities handle the rest of the centering adjustment based on the element's own size.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7nvhe77ulr9qg11mx17v.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7nvhe77ulr9qg11mx17v.gif" alt="precise-element-positioning" width="400" height="196"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Going Beyond Arbitrary Values: Custom &lt;code&gt;calc()&lt;/code&gt; Utilities
&lt;/h2&gt;

&lt;p&gt;Arbitrary values are fantastic for one-offs. But what if you find yourself repeatedly using the same &lt;code&gt;calc()&lt;/code&gt; expression? Like that &lt;code&gt;h-[calc(100vh-4rem)]&lt;/code&gt; for content below your standard header? Typing that out every time is tedious and error-prone.&lt;/p&gt;

&lt;p&gt;This is where extending your Tailwind configuration shines. You can define your own custom utilities that encapsulate these common calculations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Add this to your &lt;code&gt;tailwind.config.ts&lt;/code&gt;:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Config&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;tailwindcss&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// Example: Add custom height calculation&lt;/span&gt;
      &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;screen-minus-header&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;calc(100vh - 4rem)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Assuming 4rem header&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="c1"&gt;// Example: Add custom width calculation&lt;/span&gt;
      &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;content-area&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;calc(100% - 16rem)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Assuming 16rem sidebar&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="c1"&gt;// Example: Add custom spacing&lt;/span&gt;
      &lt;span class="na"&gt;spacing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dynamic-gutter&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;calc(1rem + 2vw)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
  &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="c1"&gt;// Ensure your content paths are configured here&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./src/**/*.{html,js,jsx,ts,tsx,vue}&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, you can use these just like native Tailwind utilities:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;main&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"h-screen-minus-header w-content-area p-dynamic-gutter"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  Look Ma, custom calc utilities!
&lt;span class="nt"&gt;&amp;lt;/main&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This makes your HTML cleaner, your calculations consistent, and your design system more robust.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;TailwindCSS provides an incredible foundation for rapid UI development. By understanding and integrating the native CSS &lt;code&gt;calc()&lt;/code&gt; function through arbitrary values and custom configurations, you unlock a whole new level of dynamic control and precision.&lt;/p&gt;

&lt;p&gt;Thank you for reading! If you found this blog post helpful, please consider sharing it with others who might benefit. Feel free to check out my other blog posts and visit my socials!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://devarshi.dev" rel="noopener noreferrer"&gt;Profile&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://linkedin.com/in/devarshi-shimpi" rel="noopener noreferrer"&gt;Linkedin&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://twitter.com/devarshishimpi" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://youtube.com/@devarshishimpi" rel="noopener noreferrer"&gt;Youtube&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://devarshishimpi.hashnode.dev" rel="noopener noreferrer"&gt;Hashnode&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/devarshishimpi"&gt;DEV&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Read more
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://devarshi.dev/blog/creating-your-first-droplet-digitalocean-tutorials" rel="noopener noreferrer"&gt;Creating Your First Droplet - DigitalOcean Tutorials&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://devarshi.dev/blog/top-ten-vscode-extensions-you-need-in-twentytwentytwo" rel="noopener noreferrer"&gt;Top 10 VS CODE Extensions You Need In 2022!&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://devarshi.dev/blog/setting-up-an-ubuntu-ec2-instance-from-scratch-on-aws" rel="noopener noreferrer"&gt;Setting Up An Ubuntu EC2 Instance From Scratch on AWS&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>webdev</category>
      <category>tutorial</category>
      <category>tailwindcss</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Introduction to Next.js 15: What's New and the improvements!</title>
      <dc:creator>Devarshi Shimpi</dc:creator>
      <pubDate>Tue, 17 Dec 2024 05:30:00 +0000</pubDate>
      <link>https://dev.to/devarshishimpi/introduction-to-nextjs-15-whats-new-and-the-improvements-1eob</link>
      <guid>https://dev.to/devarshishimpi/introduction-to-nextjs-15-whats-new-and-the-improvements-1eob</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Next.js 15 has introduced a range of exciting features and improvements that enhance both performance and developer experience. Here’s a detailed overview of what's new!&lt;/p&gt;

&lt;h2&gt;
  
  
  Seamless Upgrades with &lt;a class="mentioned-user" href="https://dev.to/next"&gt;@next&lt;/a&gt;/codemod CLI
&lt;/h2&gt;

&lt;p&gt;The enhanced codemod CLI simplifies upgrading to the latest Next.js and React versions. It automates code transformations, ensuring a smooth transition to new APIs and features.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx @next/codemod@canary upgrade latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Performance Enhancements
&lt;/h3&gt;

&lt;p&gt;Next.js 15 focuses heavily on improving the speed and efficiency of applications:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9a7g9uefe7fmvnvw08df.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9a7g9uefe7fmvnvw08df.png" alt="nextjs-15-banner" width="800" height="336"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Turbopack Boost:&lt;/strong&gt; Next.js 15's integration with Turbopack accelerates development server startup by up to 53%, providing faster feedback loops.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Hybrid Rendering:&lt;/strong&gt; Partial prerendering combines static and dynamic content for quicker initial loads and improved user experience.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Enhanced Caching:&lt;/strong&gt; Optimized caching strategies reduce network requests, resulting in faster load times and predictable data fetching.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Developer Experience (DX) Improvements
&lt;/h3&gt;

&lt;p&gt;Next.js 15 prioritizes the developer experience with several new tools and features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Asynchronous Request-Specific APIs&lt;/strong&gt;: APIs for handling cookies, headers, and search parameters have been updated to support asynchronous operations. This change allows for cleaner code and enhances application responsiveness.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feawxzsexgrzuiuv5gu01.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feawxzsexgrzuiuv5gu01.png" alt="nextjs-15-hydration-error" width="800" height="545"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;New Debugging Tools&lt;/strong&gt;: Enhanced error messages and stack traces make debugging more efficient, helping developers quickly identify and resolve issues.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;New Form Component&lt;/strong&gt;: A built-in form component simplifies form handling by eliminating the need for third-party libraries. It supports automatic validation, and progressive enhancement, ensuring that forms work even without JavaScript enabled.&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Form&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;next/form&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Page&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Form&lt;/span&gt; &lt;span class="na"&gt;action&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"/search"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"query"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Submit&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Form&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Experimental Authorization APIs&lt;/strong&gt;: New APIs for handling authorization errors (&lt;code&gt;forbidden&lt;/code&gt; and &lt;code&gt;unauthorized&lt;/code&gt;) provide granular control over authentication processes.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;TypeScript Configuration Support&lt;/strong&gt;: Developers can now use TypeScript for their configuration files by renaming &lt;code&gt;next.config.js&lt;/code&gt; to &lt;code&gt;next.config.ts&lt;/code&gt;. This feature improves type safety and autocompletion, reducing errors during development.&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;NextConfig&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;next&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;nextConfig&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;NextConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="cm"&gt;/* config options here */&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;nextConfig&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Static Route Indicator
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffd1d6a5er2qrwyaae0ma.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffd1d6a5er2qrwyaae0ma.png" alt="nextjs-15-static-indicator" width="800" height="240"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next.js now displays a Static Route Indicator during development to help you identify which routes are static or dynamic. This visual cue makes it easier to optimize performance by understanding how your pages are rendered.&lt;/p&gt;

&lt;h3&gt;
  
  
  New Middleware Capabilities
&lt;/h3&gt;

&lt;p&gt;A standout feature in Next.js 15 is the introduction of middleware, which allows developers to run code before requests are completed. This is particularly useful for tasks such as authentication and logging.&lt;/p&gt;

&lt;h3&gt;
  
  
  Support for React 19 and ESLint 9
&lt;/h3&gt;

&lt;p&gt;Next.js 15 officially supports React 19, which introduces several performance optimizations and new features including the new &lt;strong&gt;React Compiler&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;It also adds support for ESLint 9 while maintaining backward compatibility with ESLint 8. This ensures better code quality through improved linting capabilities tailored for React hooks usage.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Next.js 15 represents a significant advancement in web development capabilities with its blend of performance enhancements, improved developer tools, and robust support for modern React features.&lt;/p&gt;

&lt;p&gt;Thank you for reading! If you found this blog post helpful, please consider sharing it with others who might benefit. Feel free to check out my other blog posts and visit my socials!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://devarshi.dev" rel="noopener noreferrer"&gt;Profile&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://linkedin.com/in/devarshi-shimpi" rel="noopener noreferrer"&gt;Linkedin&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://twitter.com/devarshishimpi" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://youtube.com/@devarshishimpi" rel="noopener noreferrer"&gt;Youtube&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://devarshishimpi.hashnode.dev" rel="noopener noreferrer"&gt;Hashnode&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/devarshishimpi"&gt;DEV&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Read more
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://devarshi.dev/blog/what-are-docker-images-and-how-to-use-them" rel="noopener noreferrer"&gt;What Are Docker Images And How To Use Them&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://devarshi.dev/blog/creating-your-first-droplet-digitalocean-tutorials" rel="noopener noreferrer"&gt;Creating Your First Droplet - DigitalOcean Tutorials&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://devarshi.dev/blog/setting-up-an-ubuntu-ec2-instance-from-scratch-on-aws" rel="noopener noreferrer"&gt;Setting Up An Ubuntu EC2 Instance From Scratch on AWS&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>nextjs</category>
      <category>webdev</category>
      <category>javascript</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Managing Dependencies and Security in JavaScript with NPM</title>
      <dc:creator>Devarshi Shimpi</dc:creator>
      <pubDate>Fri, 09 Aug 2024 13:13:19 +0000</pubDate>
      <link>https://dev.to/devarshishimpi/managing-dependencies-and-security-in-javascript-with-npm-2k6m</link>
      <guid>https://dev.to/devarshishimpi/managing-dependencies-and-security-in-javascript-with-npm-2k6m</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;The JavaScript ecosystem, particularly with &lt;strong&gt;NPM&lt;/strong&gt;, &lt;em&gt;is both a boon and a bane for developers&lt;/em&gt;. On one hand, it provides an incredibly vast library of modules that can accelerate development. On the other hand, it can feel like a tangled web of dependencies, raising legitimate security concerns. Let's dive deep into the intricacies of NPM, the security implications, and how modern bundlers mitigate these risks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding NPM and Its Ecosystem
&lt;/h2&gt;

&lt;p&gt;NPM is the default package manager for Node.js, which is widely used for server-side and full-stack JavaScript development. It allows developers to easily install, share, and manage code modules.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flzp0qf5xg6rrlmhryo4h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flzp0qf5xg6rrlmhryo4h.png" alt="npm-lp" width="800" height="366"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The convenience it offers has made it a cornerstone of modern JavaScript development. However, with great convenience comes great responsibility.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Dependency Hell
&lt;/h3&gt;

&lt;p&gt;When you set up a basic project with &lt;strong&gt;NPM&lt;/strong&gt;, you often find yourself pulling in numerous dependencies. Each dependency can have its own dependencies, creating a cascading effect that can quickly spiral out of control. This phenomenon is often referred to as &lt;strong&gt;dependency hell&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Consider this scenario:&lt;/strong&gt; You install a popular library like &lt;a href="https://www.npmjs.com/package/tailwindcss" rel="noopener noreferrer"&gt;TailwindCSS&lt;/a&gt;. TailwindCSS itself has a few dependencies.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffpuokc87y7rcrtr2o5o7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffpuokc87y7rcrtr2o5o7.png" alt="tailwind-npm-depend" width="800" height="395"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Those dependencies, in turn, have their own dependencies, and so on. Before you know it, your project has hundreds of packages installed, many of which you may not be familiar with. This can make your project more vulnerable to security risks, as it’s challenging to vet each package and its updates.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F63p4750ro6ztutf3fwuz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F63p4750ro6ztutf3fwuz.png" alt="tailwind-depend-graph" width="800" height="550"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Security Concerns
&lt;/h2&gt;

&lt;p&gt;One of the primary security concerns with NPM is the potential for malicious code to be introduced into your project through dependencies. This can happen in a few ways:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Compromised Packages&lt;/strong&gt;: A legitimate package you depend on might get compromised. This can happen if a maintainer's account is hacked or if the maintainer themselves goes rogue.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Typosquatting&lt;/strong&gt;: Malicious actors sometimes publish packages with names similar to popular packages, hoping that developers will mistype the name and inadvertently install the malicious package.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Dependency Chains&lt;/strong&gt;: Even if your direct dependencies are secure, they might depend on other packages that are not. This creates a long chain of trust that can be difficult to manage.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  The Polyfill.io Incident
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5wlr2nhpvn1froxwzoqh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5wlr2nhpvn1froxwzoqh.png" alt="polyfill-article-link" width="800" height="442"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Link to the article &lt;a href="https://arcticwolf.com/resources/blog/polyfill-supply-chain-attack-impacts-100k-sites/" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A recent incident involving a service called Polyfill.io highlighted these risks. &lt;strong&gt;Polyfill.io&lt;/strong&gt; is a &lt;strong&gt;CDN service&lt;/strong&gt; that provides &lt;em&gt;JavaScript polyfills for older browsers&lt;/em&gt;. It was discovered that this service could potentially serve different versions of the same script based on the browser's user agent. This variability made it impossible to use integrity checks to verify the script, creating a security vulnerability.&lt;/p&gt;

&lt;p&gt;Many websites, including high-profile ones like Hulu, were loading scripts from this service. This incident underscores the dangers of relying on third-party CDNs for critical scripts, as a compromise in the CDN can lead to widespread vulnerabilities.&lt;/p&gt;

&lt;h3&gt;
  
  
  So, Are We Trading Security for Convenience?
&lt;/h3&gt;

&lt;p&gt;Yes, managing dependencies in a large software project is inherently complex, and no system is foolproof. However, modern package managers, bundlers, and security best practices provide robust safeguards against most common attack vectors.&lt;/p&gt;

&lt;p&gt;The key takeaway is this: understand how your tools work. Don't blindly trust; verify. Pin your dependency versions, audit your dependencies regularly, and stay informed about potential vulnerabilities. By adopting a proactive security mindset and leveraging the tools at your disposal, you can reap the benefits of a vibrant package ecosystem without compromising the integrity of your code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Modern Bundlers to the Rescue
&lt;/h2&gt;

&lt;p&gt;Fortunately, modern JavaScript development has tools to mitigate these risks. Bundlers like Webpack, Vite, and Rollup play a crucial role in ensuring the security and efficiency of web applications.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is a Bundler?
&lt;/h3&gt;

&lt;p&gt;A bundler takes the various modules and dependencies in your project and combines them into a single file (or a few files) that can be efficiently loaded by the browser. This process not only improves performance by reducing the &lt;em&gt;number of HTTP requests&lt;/em&gt; but also helps in managing dependencies more securely.&lt;/p&gt;

&lt;h3&gt;
  
  
  How Bundlers Improve Security
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Local Assets&lt;/strong&gt;: When you build your project using a bundler, it compiles all your code and dependencies into a single bundle. This means that your application is &lt;strong&gt;serving all its JavaScript&lt;/strong&gt; from &lt;strong&gt;your own domain&lt;/strong&gt;, &lt;strong&gt;not relying on third-party CDNs&lt;/strong&gt;. This significantly reduces the risk of third-party script compromises.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Lock Files&lt;/strong&gt;: NPM generates a &lt;code&gt;package-lock.json&lt;/code&gt; file that locks the versions of all installed packages. This ensures that the same versions are used every time someone installs your project, even if newer versions of the dependencies are released. This consistency is crucial for security.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9mugxk4drwctfvzd5ab4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9mugxk4drwctfvzd5ab4.png" alt="integrity-package-lock" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Integrity Checks&lt;/strong&gt;: Modern build tools can include integrity checks to ensure that the files served to the browser match the expected hashes. This makes it harder for compromised or altered packages to go unnoticed.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Example with Vite
&lt;/h3&gt;

&lt;p&gt;Let's take Vite, a modern bundler, as an example. When you install dependencies and build your project with Vite, it bundles all the necessary JavaScript files into a single minified file. Here’s a step-by-step of what happens:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0pzh46empe9ujphehvlv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0pzh46empe9ujphehvlv.png" alt="vite-lp" width="800" height="469"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Install Dependencies&lt;/strong&gt;: You install your dependencies using NPM.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Build the Project&lt;/strong&gt;: You run &lt;code&gt;vite build&lt;/code&gt;, which compiles your project.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Serve Local Assets&lt;/strong&gt;: When you deploy your project, all the JavaScript files are served from your own domain. No external CDNs are involved.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This process ensures that the JavaScript running on your site is exactly what you expect, with no surprises from third-party CDNs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Addressing Common Misconceptions
&lt;/h2&gt;

&lt;p&gt;There are several misconceptions about NPM and modern JavaScript development that contribute to the fear of security risks. Let’s address some of these:&lt;/p&gt;

&lt;h3&gt;
  
  
  Misconception 1: NPM Directly Serves Files to Users
&lt;/h3&gt;

&lt;p&gt;Some believe that when users visit a site, they are directly fetching files from NPM. This is not true. NPM is used to manage and install dependencies during the development process. When you build and deploy your project, all the code is served from your own servers, not from NPM.&lt;/p&gt;

&lt;h3&gt;
  
  
  Misconception 2: Dev Dependencies are Included in Production
&lt;/h3&gt;

&lt;p&gt;Another common misconception is that dev dependencies (packages used for development purposes, like testing or linting tools) are included in the final bundle served to users. This is false. Dev dependencies are only used during development and are not included in the production build.&lt;/p&gt;

&lt;h3&gt;
  
  
  Misconception 3: Package Lock Files are Unnecessary
&lt;/h3&gt;

&lt;p&gt;Some developers overlook the importance of &lt;code&gt;package-lock.json&lt;/code&gt; files. These files ensure that the exact same versions of dependencies are used every time the project is installed, which is crucial for maintaining security and consistency. Without a lock file, there’s a risk of pulling in newer, potentially compromised versions of dependencies.&lt;/p&gt;

&lt;h2&gt;
  
  
  Best Practices for Secure JavaScript Development
&lt;/h2&gt;

&lt;p&gt;To further mitigate security risks, follow these best practices:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Regular Audits&lt;/strong&gt;: Regularly audit your dependencies using tools like &lt;code&gt;npm audit&lt;/code&gt; to identify and address vulnerabilities.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Lock File Maintenance&lt;/strong&gt;: Always use and maintain your &lt;code&gt;package-lock.json&lt;/code&gt; file. Ensure that your CI/CD pipelines respect the lock file to prevent accidental upgrades.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Dependency Management&lt;/strong&gt;: Be mindful of the dependencies you add to your project. Avoid unnecessary dependencies and consider the reputation and activity of the package maintainers.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Use Trusted Sources&lt;/strong&gt;: Prefer well-established libraries and avoid packages with unclear or questionable histories.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Stay Updated&lt;/strong&gt;: Keep your dependencies up to date with the latest security patches. Tools like Dependabot can automate this process for you.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;By taking proactive steps to secure your dependencies and being aware of how your tools work, you can harness the power of NPM and modern JavaScript development while keeping your projects safe and secure.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The NPM ecosystem, with its vast library of packages, undeniably brings convenience to JavaScript development. However, it also introduces potential security risks that cannot be ignored. Understanding how modern bundlers work, maintaining a strict dependency management strategy, and following best practices can significantly mitigate these risks.&lt;/p&gt;

&lt;p&gt;Thank you for reading! If you found this blog post helpful, please consider sharing it with others who might benefit. Feel free to check out my other blog posts and visit my socials!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://bio.link/devarshishimpi" rel="noopener noreferrer"&gt;Profile&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://linkedin.com/in/devarshi-shimpi" rel="noopener noreferrer"&gt;Linkedin&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://twitter.com/devarshishimpi" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://youtube.com/@devarshishimpi" rel="noopener noreferrer"&gt;Youtube&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://devarshishimpi.hashnode.dev" rel="noopener noreferrer"&gt;Hashnode&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/devarshishimpi"&gt;DEV&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Read more
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://devarshi.dev/blog/how-to-create-a-new-nextjs-project-with-cf-workers-and-pages" rel="noopener noreferrer"&gt;How to create a new Next.js project with Cloudflare Pages and Workers&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://devarshi.dev/blog/easiest-way-to-install-wordpress-for-beginners" rel="noopener noreferrer"&gt;Easiest Way to Install WordPress Locally for Beginners&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://devarshi.dev/blog/wordpresscom-wordpressorg-the-differences-which-to-choose" rel="noopener noreferrer"&gt;WordPress.com vs. WordPress.org The Differences &amp;amp; Which To Choose&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>node</category>
      <category>beginners</category>
    </item>
    <item>
      <title>The End of Google URL Shortener and breaks 4.6 BILLION URLs!</title>
      <dc:creator>Devarshi Shimpi</dc:creator>
      <pubDate>Thu, 01 Aug 2024 11:18:20 +0000</pubDate>
      <link>https://dev.to/devarshishimpi/the-end-of-google-url-shortener-and-breaks-46-billion-urls-4o1</link>
      <guid>https://dev.to/devarshishimpi/the-end-of-google-url-shortener-and-breaks-46-billion-urls-4o1</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;The innovations Google has brought to the table, from Chrome to the Google ecosystem, have undeniably shaped the internet. But sometimes, their decisions leave us scratching our heads, frustrated and disappointed. One such decision is the end of &lt;code&gt;goo.gl&lt;/code&gt;, their URL shortener service, which brings back memories of other products they have shelved over the years—like Google Reader and, more recently, Google Domains.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Rise and Fall of goo.gl
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0rkdulutlfi16d0hws22.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0rkdulutlfi16d0hws22.png" alt="goog-url-shortener-logo" width="800" height="447"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When &lt;a href="https://goo.gl" rel="noopener noreferrer"&gt;goo.gl&lt;/a&gt; was launched, it was a nifty tool that made sharing long, cumbersome URLs a breeze. It offered analytics and QR codes, making it a favorite among both casual users and developers.&lt;/p&gt;

&lt;p&gt;But in 2018, Google announced that it would no longer support the creation of new goo.gl links, nudging users towards alternatives like &lt;a href="https://firebase.google.com/docs/dynamic-links" rel="noopener noreferrer"&gt;Firebase Dynamic Links&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Fast forward to now, and we've learned that all goo.gl links will completely stop working by August 2025. This isn’t just about not being able to make new links anymore; it means that all existing links will die. Clicking on them after the cutoff date will lead to a 404 error -- a digital dead end.&lt;/p&gt;

&lt;h2&gt;
  
  
  Alternatives to Google URL Shortener
&lt;/h2&gt;

&lt;p&gt;Google suggested &lt;a href="(https://firebase.google.com/docs/dynamic-links)"&gt;Firebase Dynamic Links&lt;/a&gt; as an alternative, but guess what? That service is also being deprecated on August 25, 2025. It's a frustrating loop, recommending one product as a replacement only to kill it off later. It makes planning and continuity a nightmare for users.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv3b7stqamawxipspfdoz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv3b7stqamawxipspfdoz.png" alt="dub-co-homepage" width="800" height="482"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As goo.gl sunsets, users are left seeking alternatives. Services like Bitly, TinyURL, and &lt;a href="https://dub.co" rel="noopener noreferrer"&gt;dub.co&lt;/a&gt; offer similar functionalities, each service comes with its own set of features, policies, and levels of reliability.&lt;/p&gt;

&lt;p&gt;I personally prefer &lt;a href="https://dub.co" rel="noopener noreferrer"&gt;dub.co&lt;/a&gt; and it has been great in my experience till now. Their team was quick enough to even make the migration process as seamless as possible! Especially their quick alternative as &lt;strong&gt;ggl.link&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnlcoz2w558ta0hi1u993.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnlcoz2w558ta0hi1u993.png" alt="dub-co-blog-google" width="800" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The decision to end goo.gl is a reminder of the impermanence of digital tools. While Google's decision might be based on practical considerations, the impact on users is real and far-reaching. It disrupts current operations, potentially erases valuable information, and once again highlights the precariousness of relying on a single company for critical digital infrastructure.&lt;/p&gt;

&lt;p&gt;Thank you for reading! If you found this blog post helpful, please consider sharing it with others who might benefit. Feel free to check out my other blog posts and visit my socials!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://bio.link/devarshishimpi" rel="noopener noreferrer"&gt;Profile&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://linkedin.com/in/devarshi-shimpi" rel="noopener noreferrer"&gt;Linkedin&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://twitter.com/devarshishimpi" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://youtube.com/@devarshishimpi" rel="noopener noreferrer"&gt;Youtube&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://devarshishimpi.hashnode.dev" rel="noopener noreferrer"&gt;Hashnode&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/devarshishimpi"&gt;DEV&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Read more
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://devarshi.dev/blog/what-are-docker-images-and-how-to-use-them" rel="noopener noreferrer"&gt;What Are Docker Images And How To Use Them&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://devarshi.dev/blog/creating-your-first-droplet-digitalocean-tutorials" rel="noopener noreferrer"&gt;Creating Your First Droplet - DigitalOcean Tutorials&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://devarshi.dev/blog/setting-up-an-ubuntu-ec2-instance-from-scratch-on-aws" rel="noopener noreferrer"&gt;Setting Up An Ubuntu EC2 Instance From Scratch on AWS&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>news</category>
      <category>google</category>
      <category>webdev</category>
      <category>programming</category>
    </item>
    <item>
      <title>How to create a new Next.js project with Cloudflare Pages and Workers</title>
      <dc:creator>Devarshi Shimpi</dc:creator>
      <pubDate>Thu, 18 Jul 2024 14:58:33 +0000</pubDate>
      <link>https://dev.to/devarshishimpi/how-to-create-a-new-nextjs-project-with-cloudflare-pages-and-workers-43d9</link>
      <guid>https://dev.to/devarshishimpi/how-to-create-a-new-nextjs-project-with-cloudflare-pages-and-workers-43d9</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In this guide, we'll walk through creating a new Next.js 14 project using TypeScript and deploying it on Cloudflare Pages with Workers for a full-stack solution. We'll use the &lt;code&gt;create-cloudflare&lt;/code&gt; CLI (C3) to set up our project, and deploy the application.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Create a new Git repository
&lt;/h3&gt;

&lt;p&gt;Go to &lt;a href="https://github.com/" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;, click the "+" icon in the top right, and select &lt;strong&gt;"New repository"&lt;/strong&gt;. Set the repository name to &lt;code&gt;nextjs-cf-pages-workers&lt;/code&gt; or whatever you would like to, choose Public or Private visibility, and click &lt;strong&gt;"Create repository"&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxgba2a4y2mjjxwafm6a6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxgba2a4y2mjjxwafm6a6.png" alt="new-git-repo" width="800" height="644"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Your new repository is now ready! Clone the repository and get ready for the next step!&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Create a New Next.js Project
&lt;/h3&gt;

&lt;p&gt;To get started, we'll use the &lt;code&gt;create-cloudflare&lt;/code&gt; CLI (C3) to create a new Next.js project. Open your terminal and run the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm create cloudflare@latest my-next-app &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="nt"&gt;--framework&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;next
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command initializes a new Next.js project in a directory named &lt;code&gt;my-next-app&lt;/code&gt;. Follow the prompts to configure your project. The CLI will install the necessary dependencies, including the Wrangler CLI and the &lt;code&gt;@cloudflare/next-on-pages&lt;/code&gt; adapter.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa1t85u4ryt7mk4din5va.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa1t85u4ryt7mk4din5va.png" alt="npm-create-cloudflare" width="800" height="309"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In your terminal, this is how it should look like. After the project is created, navigate to the project directory, and run &lt;code&gt;npm run dev&lt;/code&gt;. You should have a browser preview like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg485zdtg4wihk4b3729j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg485zdtg4wihk4b3729j.png" alt="next-browser-preview" width="800" height="438"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Configure the Project for Cloudflare Pages
&lt;/h3&gt;

&lt;p&gt;You can deploy using mutliple options, I have covered 2 in this article.&lt;/p&gt;

&lt;p&gt;Log in to the Cloudflare dashboard and select your account. Navigate to Workers &amp;amp; Pages &amp;gt; Create application &amp;gt; Pages &amp;gt; Connect to Git. Authorize access to your GitHub account and select the new repository you created.&lt;/p&gt;

&lt;p&gt;Make sure you have added &lt;code&gt;nodejs_compat&lt;/code&gt; as the compatibility flags or the build will fail!&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Configure and Deploy Without C3
&lt;/h3&gt;

&lt;p&gt;If you prefer not to use C3, you can manually configure and deploy your project. Install the &lt;code&gt;@cloudflare/next-on-pages&lt;/code&gt; adapter:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; @cloudflare/next-on-pages
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 5: Set Up Bindings for Local Development
&lt;/h3&gt;

&lt;p&gt;Bindings allow your application to interact with Cloudflare developer products. To set up bindings for local development, modify your Next.js configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// next.config.mjs&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;setupDevPlatform&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@cloudflare/next-on-pages/next-dev&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NODE_ENV&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;development&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;setupDevPlatform&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cm"&gt;/** @type {import('next').NextConfig} */&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;nextConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;nextConfig&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ensure you have a &lt;code&gt;wrangler.toml&lt;/code&gt; file at the root of your project with a KV binding:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="c"&gt;# wrangler.toml&lt;/span&gt;
&lt;span class="py"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"my-next-app"&lt;/span&gt;
&lt;span class="py"&gt;compatibility_flags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"nodejs_compat"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="nn"&gt;[[kv_namespaces]]&lt;/span&gt;
&lt;span class="py"&gt;binding&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"MY_KV"&lt;/span&gt;
&lt;span class="py"&gt;id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"&amp;lt;YOUR_KV_NAMESPACE_ID&amp;gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Alternatively, you can run the following command and it should deploy the project as shown below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run deploy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4qwnbyzoc34h443mcuaz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4qwnbyzoc34h443mcuaz.png" alt="cf-deploy-terminal" width="800" height="190"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Success!
&lt;/h2&gt;

&lt;p&gt;By following these steps, you've successfully created and deployed a full-stack Next.js 14 project with TypeScript on Cloudflare Pages and Workers. This setup leverages Cloudflare's powerful developer products to enhance your application's capabilities.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy6ykaox0sbrxwjmgkkxq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy6ykaox0sbrxwjmgkkxq.png" alt="cf-dashboard" width="800" height="488"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Thank you for reading! If you found this blog post helpful, please consider sharing it with others who might benefit. Feel free to check out my other blog posts and visit my socials!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://bio.link/devarshishimpi" rel="noopener noreferrer"&gt;Profile&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://linkedin.com/in/devarshi-shimpi" rel="noopener noreferrer"&gt;Linkedin&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://twitter.com/devarshishimpi" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://youtube.com/@devarshishimpi" rel="noopener noreferrer"&gt;Youtube&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://devarshishimpi.hashnode.dev" rel="noopener noreferrer"&gt;Hashnode&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/devarshishimpi"&gt;DEV&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Read more
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://devarshi.dev/blog/what-are-docker-images-and-how-to-use-them" rel="noopener noreferrer"&gt;What Are Docker Images And How To Use Them&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://devarshi.dev/blog/creating-your-first-droplet-digitalocean-tutorials" rel="noopener noreferrer"&gt;Creating Your First Droplet - DigitalOcean Tutorials&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://devarshi.dev/blog/setting-up-an-ubuntu-ec2-instance-from-scratch-on-aws" rel="noopener noreferrer"&gt;Setting Up An Ubuntu EC2 Instance From Scratch on AWS&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>beginners</category>
      <category>webdev</category>
      <category>javascript</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Managing Metadata in Next.js for Enhanced SEO and User Experience</title>
      <dc:creator>Devarshi Shimpi</dc:creator>
      <pubDate>Tue, 02 Jul 2024 11:13:43 +0000</pubDate>
      <link>https://dev.to/devarshishimpi/managing-metadata-in-nextjs-14-for-enhanced-seo-and-user-experience-3aam</link>
      <guid>https://dev.to/devarshishimpi/managing-metadata-in-nextjs-14-for-enhanced-seo-and-user-experience-3aam</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In Next.js 14, there are two primary ways to manage metadata for your application: using a static &lt;code&gt;metadata&lt;/code&gt; object or a dynamic &lt;code&gt;generateMetadata&lt;/code&gt; function. Below is a detailed guide on how to utilize both options effectively.&lt;/p&gt;

&lt;p&gt;This guide provides an in-depth look at both approaches, helping you understand how to effectively implement metadata in your Next.js application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next.js Demo with Metadata Output
&lt;/h2&gt;

&lt;p&gt;In this live example, we'll create a simple Next.js application showcasing the usage of metadata.  Then, we'll view the rendered output in the browser and visualise the metadata in action.&lt;/p&gt;

&lt;p&gt;We start by creating a new Next.js project with &lt;code&gt;create-next-app&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx create-next-app@latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's the options I chose while creating it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy5hn3c2c4gbo49ujcmen.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy5hn3c2c4gbo49ujcmen.png" alt="create-next-app-choices" width="800" height="226"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Add this sample code in the following block and let's preview it in the browser.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Code:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4dplnm94rwbwm2e0jl7q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4dplnm94rwbwm2e0jl7q.png" alt="nextjs-metadata-code" width="800" height="438"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Output:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;And here's the browser output with get with the tags, keep reading the entire article to see how you could implement in your code.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqdx7yd4pwc0tlenu4rgi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqdx7yd4pwc0tlenu4rgi.png" alt="nextjs-metadata-output" width="800" height="387"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Static Metadata
&lt;/h3&gt;

&lt;p&gt;To define static metadata, you export a &lt;code&gt;Metadata&lt;/code&gt; object from a &lt;code&gt;layout.ts&lt;/code&gt; or &lt;code&gt;page.ts&lt;/code&gt; file using the App Router.&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Metadata&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;next&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Metadata&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Static Page Title&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;This is a static page description&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Page&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This method is suitable when the metadata does not depend on runtime information.&lt;/p&gt;

&lt;h3&gt;
  
  
  Dynamic Metadata
&lt;/h3&gt;

&lt;p&gt;Dynamic metadata allows you to generate metadata based on dynamic data, such as route parameters or fetched data.&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Metadata&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ResolvingMetadata&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;next&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Props&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nl"&gt;searchParams&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;generateMetadata&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;searchParams&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="nx"&gt;Props&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ResolvingMetadata&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Metadata&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;product&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`https://api.example.com/products/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;previousImages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;openGraph&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;images&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;openGraph&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;images&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;imageUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;previousImages&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Page&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;searchParams&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="nx"&gt;Props&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This method is useful when the metadata depends on dynamic data or needs to extend parent metadata.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Points
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Server Components Only&lt;/strong&gt;: Both &lt;code&gt;metadata&lt;/code&gt; and &lt;code&gt;generateMetadata&lt;/code&gt; are only supported in Server Components.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Single Export Rule&lt;/strong&gt;: You cannot export both the &lt;code&gt;metadata&lt;/code&gt; object and &lt;code&gt;generateMetadata&lt;/code&gt; function from the same route segment.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Metadata Fields
&lt;/h3&gt;

&lt;p&gt;Next.js provides a variety of metadata fields to customize the metadata of your application comprehensively.&lt;/p&gt;

&lt;h4&gt;
  
  
  Title
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;String&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;  &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Metadata&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Next.js Application&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output: &lt;code&gt;&amp;lt;title&amp;gt;Next.js Application&amp;lt;/title&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Template Object&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;  &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Metadata&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;%s | Next.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Next.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output in child routes: &lt;code&gt;&amp;lt;title&amp;gt;About | Next.js&amp;lt;/title&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Absolute&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;  &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Metadata&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;absolute&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;About Us&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output: &lt;code&gt;&amp;lt;title&amp;gt;About Us&amp;lt;/title&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Description
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Metadata&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;The React Framework for the Web&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output: &lt;code&gt;&amp;lt;meta name="description" content="The React Framework for the Web" /&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Basic Fields
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Metadata&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;generator&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Next.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;applicationName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Next.js App&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;keywords&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Next.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;React&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;JavaScript&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;authors&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Author Name&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://example.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"generator"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"Next.js"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"application-name"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"Next.js App"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"keywords"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"Next.js,React,JavaScript"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"author"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"Author Name"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"author"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://example.com"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Open Graph
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Metadata&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;openGraph&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Next.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;The React Framework for the Web&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://nextjs.org&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;siteName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Next.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;images&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://nextjs.org/og.png&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;800&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;600&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;locale&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;en_US&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;website&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;property=&lt;/span&gt;&lt;span class="s"&gt;"og:title"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"Next.js"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;property=&lt;/span&gt;&lt;span class="s"&gt;"og:description"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"The React Framework for the Web"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;property=&lt;/span&gt;&lt;span class="s"&gt;"og:url"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"https://nextjs.org/"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;property=&lt;/span&gt;&lt;span class="s"&gt;"og:site_name"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"Next.js"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;property=&lt;/span&gt;&lt;span class="s"&gt;"og:locale"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"en_US"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;property=&lt;/span&gt;&lt;span class="s"&gt;"og:image"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"https://nextjs.org/og.png"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;property=&lt;/span&gt;&lt;span class="s"&gt;"og:image:width"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"800"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;property=&lt;/span&gt;&lt;span class="s"&gt;"og:image:height"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"600"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Twitter
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Metadata&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;twitter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;card&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;summary_large_image&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Next.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;The React Framework for the Web&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;images&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://nextjs.org/og.png&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"twitter:card"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"summary_large_image"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"twitter:title"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"Next.js"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"twitter:description"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"The React Framework for the Web"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"twitter:image"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"https://nextjs.org/og.png"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Robots
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Metadata&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;robots&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;follow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;googleBot&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;follow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"robots"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"noindex, follow"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"googlebot"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"index, nofollow"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  MetadataBase
&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;metadataBase&lt;/code&gt; allows setting a base URL prefix for metadata fields that require a fully qualified URL.&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Metadata&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;metadataBase&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://example.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="na"&gt;openGraph&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;images&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/og-image.png&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;property=&lt;/span&gt;&lt;span class="s"&gt;"og:image"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"https://example.com/og-image.png"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Best Practices
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Use Static Metadata When Possible&lt;/strong&gt;: If metadata doesn't depend on runtime information, prefer static metadata for simplicity.&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Dynamic Metadata for Dynamic Data&lt;/strong&gt;: Use &lt;code&gt;generateMetadata&lt;/code&gt; for routes where metadata depends on dynamic data or needs to extend parent metadata.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Combine Metadata Fields&lt;/strong&gt;: Utilize a combination of different metadata fields to ensure comprehensive metadata coverage for SEO and social media integration.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Utilize &lt;code&gt;metadataBase&lt;/code&gt;&lt;/strong&gt;: Set a &lt;code&gt;metadataBase&lt;/code&gt; in your root layout to simplify URL-based metadata field definitions.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;By following these guidelines, you can effectively manage metadata in your Next.js 14 application, enhancing both SEO and user experience.&lt;/p&gt;

&lt;p&gt;Thank you for reading! If you found this blog post helpful, please consider sharing it with others who might benefit. Feel free to check out my other blog posts and visit my socials!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://bio.link/devarshishimpi" rel="noopener noreferrer"&gt;Profile&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://linkedin.com/in/devarshi-shimpi" rel="noopener noreferrer"&gt;Linkedin&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://twitter.com/devarshishimpi" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://youtube.com/@devarshishimpi" rel="noopener noreferrer"&gt;Youtube&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://devarshishimpi.hashnode.dev" rel="noopener noreferrer"&gt;Hashnode&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/devarshishimpi"&gt;DEV&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Read more
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://devarshi.dev/blog/what-are-docker-images-and-how-to-use-them" rel="noopener noreferrer"&gt;What Are Docker Images And How To Use Them&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://devarshi.dev/blog/creating-your-first-droplet-digitalocean-tutorials" rel="noopener noreferrer"&gt;Creating Your First Droplet - DigitalOcean Tutorials&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://devarshi.dev/blog/setting-up-an-ubuntu-ec2-instance-from-scratch-on-aws" rel="noopener noreferrer"&gt;Setting Up An Ubuntu EC2 Instance From Scratch on AWS&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>nextjs</category>
      <category>webdev</category>
      <category>beginners</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Google's AI-Powered Cloud IDE Project IDX Goes Open Beta!</title>
      <dc:creator>Devarshi Shimpi</dc:creator>
      <pubDate>Mon, 10 Jun 2024 09:39:50 +0000</pubDate>
      <link>https://dev.to/devarshishimpi/googles-ai-powered-cloud-ide-project-idx-goes-open-beta-3lgl</link>
      <guid>https://dev.to/devarshishimpi/googles-ai-powered-cloud-ide-project-idx-goes-open-beta-3lgl</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;At the &lt;strong&gt;2024 Google I/O developer conference&lt;/strong&gt;, Google unveiled an exciting new tool for developers—Project IDX. Now in open beta, Project IDX is set to revolutionise the way we approach web and mobile app development.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnzr7us5x0pstlb3aato0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnzr7us5x0pstlb3aato0.png" alt="project-idx-demo" width="800" height="414"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With its AI-powered features and seamless cloud integration, this web-based integrated development environment (IDE) promises to simplify and enhance the development workflow.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Project IDX?
&lt;/h2&gt;

&lt;p&gt;Project IDX is a next-generation, web-based IDE that aims to streamline the development process. Built on the popular Code OSS project and leveraging the robust infrastructure of Google Cloud, Project IDX offers a fully configurable virtual machine (VM) environment right in your browser.&lt;/p&gt;

&lt;p&gt;For more details and to join the open beta, visit the &lt;a href="https://idx.google.com" rel="noopener noreferrer"&gt;Project IDX website&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1k560e77w7lbs66eb6kq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1k560e77w7lbs66eb6kq.png" alt="idx-frontpage" width="800" height="335"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This setup ensures reliability, safety, and complete configurability, making it an ideal choice for developers of all levels.&lt;/p&gt;

&lt;h2&gt;
  
  
  Making the Most of Project IDX
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Get Started Quickly&lt;/strong&gt;&lt;br&gt;
Start new projects or import existing ones from GitHub effortlessly. Use pre-configured templates and choose from popular frameworks and languages like JavaScript, Dart, and soon Python and Go.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Leverage IDX AI for Enhanced Coding&lt;/strong&gt;&lt;br&gt;
Utilize IDX AI for intelligent code completion, translations, and explanations of complex snippets, boosting coding speed and quality.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Utilize Built-In Emulators for Mobile Development&lt;/strong&gt;&lt;br&gt;
Test your mobile apps with built-in Android emulators and iOS simulators, previewing changes in real-time within Project IDX.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Seamless Deployment with Firebase Hosting&lt;/strong&gt;&lt;br&gt;
Deploy web and Flutter projects directly to Firebase Hosting with a few clicks, ensuring fast, secure, and global hosting.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Collaborate with Ease&lt;/strong&gt;&lt;br&gt;
Use the experimental collaborative workspace sharing to invite team members, enabling real-time collaboration on code, terminals, emulators, and more.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Google's Project IDX is set to transform the development landscape with its innovative, AI-powered, and cloud-based IDE. Now in open beta, it's the perfect time to explore its capabilities and integrate it into your development workflow.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fstogj7iuclvp5e2s4c14.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fstogj7iuclvp5e2s4c14.png" alt="idx-newproj" width="800" height="370"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thank you for reading! If you found this blog post helpful, please consider sharing it with others who might benefit. Feel free to check out my other blog posts and visit my socials!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://bio.link/devarshishimpi" rel="noopener noreferrer"&gt;Profile&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://linkedin.com/in/devarshi-shimpi" rel="noopener noreferrer"&gt;Linkedin&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://twitter.com/devarshishimpi" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://youtube.com/@devarshishimpi" rel="noopener noreferrer"&gt;Youtube&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://devarshishimpi.hashnode.dev" rel="noopener noreferrer"&gt;Hashnode&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/devarshishimpi"&gt;DEV&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Read more
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://devarshi.dev/blog/how-fast-is-bunsh-after-all" rel="noopener noreferrer"&gt;How Fast Is Bun.sh in 2024 After All?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://devarshi.dev/blog/how-to-install-and-setup-a-ghost-blog-on-aws-lightsail" rel="noopener noreferrer"&gt;How to Install and Set Up a Ghost Blog on AWS Lightsail&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://devarshi.dev/blog//deploy-minio-on-amazon-eks-self-host-s3-storage" rel="noopener noreferrer"&gt;Deploy MinIO on Amazon EKS and use your S3 Compatible Storage&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>webdev</category>
      <category>news</category>
      <category>idx</category>
      <category>cloud</category>
    </item>
  </channel>
</rss>
