<?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: Ayman Eldawy</title>
    <description>The latest articles on DEV Community by Ayman Eldawy (@aymaneldawy).</description>
    <link>https://dev.to/aymaneldawy</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%2F185073%2F9f223d8a-2fa1-44b5-904e-6db1d3ff91c9.jpg</url>
      <title>DEV Community: Ayman Eldawy</title>
      <link>https://dev.to/aymaneldawy</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/aymaneldawy"/>
    <language>en</language>
    <item>
      <title>What Is CSRF (Cross-Site Request Forgery)?</title>
      <dc:creator>Ayman Eldawy</dc:creator>
      <pubDate>Mon, 31 Aug 2026 11:23:43 +0000</pubDate>
      <link>https://dev.to/aymaneldawy/what-is-csrf-cross-site-request-forgery-18c1</link>
      <guid>https://dev.to/aymaneldawy/what-is-csrf-cross-site-request-forgery-18c1</guid>
      <description>&lt;p&gt;You’re logged in to your bank account.&lt;/p&gt;

&lt;p&gt;In another tab, you open a random website. Maybe it contains a malicious ad, or maybe the whole page is malicious.&lt;/p&gt;

&lt;p&gt;That second website sends a request to your bank:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;POST /transfer
amount=5000&amp;amp;to=attacker

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

&lt;/div&gt;



&lt;p&gt;You never opened the transfer page.&lt;/p&gt;

&lt;p&gt;You never clicked “Confirm.”&lt;/p&gt;

&lt;p&gt;But the request may still include your session cookie automatically.&lt;/p&gt;

&lt;p&gt;And if the bank only checks that cookie, it may treat the request as if it came from you.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;That’s Cross-Site Request Forgery, or CSRF&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The browser isn’t leaking your cookie&lt;/p&gt;

&lt;p&gt;There’s an important distinction here.&lt;/p&gt;

&lt;p&gt;The malicious website doesn’t necessarily need to read or steal your session cookie.&lt;/p&gt;

&lt;p&gt;It only needs the browser to attach it to a request sent to the vulnerable website.&lt;/p&gt;

&lt;p&gt;The attacker usually can’t read the response because of the browser’s same-origin policy. But for actions like changing an email address or transferring money, reading the response may not matter.&lt;/p&gt;

&lt;p&gt;The damage is already done.&lt;/p&gt;

&lt;p&gt;CSRF can target any state-changing action, such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Changing an email address&lt;/li&gt;
&lt;li&gt;Updating a password&lt;/li&gt;
&lt;li&gt;Transferring money&lt;/li&gt;
&lt;li&gt;Deleting an account&lt;/li&gt;
&lt;li&gt;Changing account settings&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The exact impact depends on what the vulnerable endpoint allows.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do CSRF tokens stop this?
&lt;/h2&gt;

&lt;p&gt;One of the most common defenses is an anti-CSRF token.&lt;/p&gt;

&lt;p&gt;The server generates a secret, unpredictable token associated with the user’s session and exposes it to the legitimate frontend.&lt;/p&gt;

&lt;p&gt;When the frontend sends a state-changing request, it includes that token:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;POST /transfer
X-CSRF-Token: random-secret-value
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The server then compares the received token with the expected one.&lt;/p&gt;

&lt;p&gt;If the token is missing or invalid, the request is rejected.&lt;/p&gt;

&lt;p&gt;The attacker’s website might be able to trigger a request, but it cannot obtain the valid token from your website because of the browser’s same-origin protections.&lt;/p&gt;

&lt;p&gt;That missing piece is what makes the forged request fail. CSRF tokens can be sent through hidden form fields, request bodies, or custom headers, depending on the application. OWASP’s CSRF Prevention Cheat Sheet&lt;/p&gt;

&lt;h2&gt;
  
  
  What about SameSite cookies?
&lt;/h2&gt;

&lt;p&gt;The SameSite cookie attribute tells the browser when a cookie should be included with cross-site requests.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;Set-Cookie: session=abc123; Secure; HttpOnly; SameSite=Lax

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

&lt;/div&gt;



&lt;p&gt;The available values include:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Strict&lt;/strong&gt;: the cookie is only sent with same-site requests.&lt;br&gt;
&lt;strong&gt;Lax&lt;/strong&gt;: blocks the cookie on most cross-site subrequests, but still allows it during certain top-level navigations.&lt;br&gt;
&lt;strong&gt;None&lt;/strong&gt;: allows cross-site requests and requires Secure.&lt;/p&gt;

&lt;p&gt;So SameSite makes many CSRF attacks much harder, but Lax doesn’t mean the cookie will never be included in a cross-site request. It’s better treated as one layer of protection rather than an excuse to ignore the rest of your CSRF defenses. MDN: Set-Cookie and SameSite&lt;/p&gt;

&lt;h2&gt;
  
  
  CSRF vs. XSS
&lt;/h2&gt;

&lt;p&gt;The easiest mental model is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;With XSS, the attacker gets JavaScript to run inside the trusted website.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;With CSRF, the attacker tricks the victim’s browser into sending an authenticated request to the trusted website.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;XSS can often bypass CSRF protections because the malicious code is already running inside the application’s origin.&lt;/p&gt;

&lt;p&gt;CSRF doesn’t need to steal your session.&lt;/p&gt;

&lt;p&gt;It uses the session while it’s still sitting safely inside your browser.&lt;/p&gt;

&lt;p&gt;This was post #4 in my Frontend Security series.&lt;/p&gt;

&lt;p&gt;If you’d like to catch up on the previous posts:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Content Security Policy (CSP): &lt;a href="https://dev.to/aymaneldawy/what-is-content-security-policy-csp-p6p"&gt;Read the post&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Cross-Site Scripting (XSS): &lt;a href="https://dev.to/aymaneldawy/understanding-the-different-types-of-xss-attacks-39ih"&gt;Read the post&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Where Should You Store Your JWT?: &lt;a href="https://dev.to/aymaneldawy/where-should-you-store-your-jwt-19ad/edit"&gt;Read the post&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;More frontend security topics are coming soon.&lt;/p&gt;

</description>
      <category>frontend</category>
      <category>security</category>
      <category>xss</category>
      <category>csp</category>
    </item>
    <item>
      <title>What’s the difference between &lt;img&gt; and &lt;picture&gt;?</title>
      <dc:creator>Ayman Eldawy</dc:creator>
      <pubDate>Tue, 25 Aug 2026 11:59:51 +0000</pubDate>
      <link>https://dev.to/aymaneldawy/whats-the-difference-between-and--2enb</link>
      <guid>https://dev.to/aymaneldawy/whats-the-difference-between-and--2enb</guid>
      <description>&lt;p&gt;If you’ve ever hesitated between them, this post should make the choice a little clearer.&lt;/p&gt;

&lt;p&gt;Let’s start with the one we use most: &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; element displays a single image.&lt;/p&gt;

&lt;p&gt;You give it a src, add an alt description for accessibility, and in many cases, that’s all you need.&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;img&lt;/span&gt;
  &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"image.jpg"&lt;/span&gt;
  &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;"A developer working on a laptop"&lt;/span&gt;
&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Simple? But &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; can also handle responsive images using srcset and sizes:&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;img&lt;/span&gt;
  &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"image-600w.jpg"&lt;/span&gt;
  &lt;span class="na"&gt;srcset=&lt;/span&gt;&lt;span class="s"&gt;"
    image-300w.jpg 300w,
    image-600w.jpg 600w,
    image-1200w.jpg 1200w
  "&lt;/span&gt;
  &lt;span class="na"&gt;sizes=&lt;/span&gt;&lt;span class="s"&gt;"100vw"&lt;/span&gt;
  &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;"A developer working on a laptop"&lt;/span&gt;
&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The browser can now choose the most suitable file based on factors such as the rendered image size, screen resolution, and the available candidates.&lt;/p&gt;

&lt;p&gt;The content of the image stays the same.&lt;/p&gt;

&lt;p&gt;Only the file dimensions change.&lt;/p&gt;

&lt;p&gt;So &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; is usually enough when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You need one image.&lt;/li&gt;
&lt;li&gt;You need different sizes of the same image.&lt;/li&gt;
&lt;li&gt;You don’t need to change its composition across screen sizes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then what is &lt;code&gt;&amp;lt;picture&amp;gt;&lt;/code&gt; for?&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&amp;lt;picture&amp;gt;&lt;/code&gt; lets you provide different image sources based on conditions such as viewport size or supported file format.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The important detail is that &lt;code&gt;&amp;lt;picture&amp;gt;&lt;/code&gt; doesn’t replace &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt;&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;It works with it.&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;picture&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;source&lt;/span&gt;
    &lt;span class="na"&gt;media=&lt;/span&gt;&lt;span class="s"&gt;"(min-width: 768px)"&lt;/span&gt;
    &lt;span class="na"&gt;srcset=&lt;/span&gt;&lt;span class="s"&gt;"wide-image.jpg"&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;source&lt;/span&gt;
    &lt;span class="na"&gt;media=&lt;/span&gt;&lt;span class="s"&gt;"(max-width: 767px)"&lt;/span&gt;
    &lt;span class="na"&gt;srcset=&lt;/span&gt;&lt;span class="s"&gt;"portrait-image.jpg"&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt;
    &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"default-image.jpg"&lt;/span&gt;
    &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;"A developer working on a laptop"&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/picture&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, desktop users receive a wide image, while mobile users receive a portrait version.&lt;/p&gt;

&lt;p&gt;This is known as art direction.&lt;/p&gt;

&lt;p&gt;You’re not just serving a smaller file. You’re changing the crop or composition so the image fits the layout better.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&amp;lt;picture&amp;gt;&lt;/code&gt; can also help you serve modern formats with a fallback:&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;picture&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;source&lt;/span&gt; &lt;span class="na"&gt;srcset=&lt;/span&gt;&lt;span class="s"&gt;"image.avif"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"image/avif"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;source&lt;/span&gt; &lt;span class="na"&gt;srcset=&lt;/span&gt;&lt;span class="s"&gt;"image.webp"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"image/webp"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt;
    &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"image.jpg"&lt;/span&gt;
    &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;"A developer working on a laptop"&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/picture&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The browser checks the sources in order and selects the first format it supports.&lt;/p&gt;

&lt;p&gt;If AVIF isn’t supported, it tries WebP.&lt;/p&gt;

&lt;p&gt;If neither works, the &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; provides the JPG fallback.&lt;/p&gt;

&lt;p&gt;So the choice is fairly simple:&lt;/p&gt;

&lt;p&gt;Use &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; when you’re showing the same image, even if you need it in different sizes.&lt;/p&gt;

&lt;p&gt;Use  when you need to control which image is displayed based on its format, crop, composition, or a media condition.&lt;/p&gt;

&lt;p&gt;So the difference is simpler than it first looks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; chooses the best version of an image.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;&amp;lt;picture&amp;gt;&lt;/code&gt; lets you define which image the browser should choose from.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now you can stop making the browser solve your identity crisis between the two.&lt;/p&gt;

</description>
      <category>html</category>
      <category>img</category>
      <category>browser</category>
      <category>frontend</category>
    </item>
    <item>
      <title>Does React Break HTML Rules? Let's Find Out</title>
      <dc:creator>Ayman Eldawy</dc:creator>
      <pubDate>Sat, 15 Aug 2026 08:42:08 +0000</pubDate>
      <link>https://dev.to/aymaneldawy/does-react-break-html-rules-lets-find-out-9p8</link>
      <guid>https://dev.to/aymaneldawy/does-react-break-html-rules-lets-find-out-9p8</guid>
      <description>&lt;p&gt;Can React create HTML that the browser itself wouldn't allow?&lt;/p&gt;

&lt;p&gt;Sounds wrong, right?&lt;/p&gt;

&lt;p&gt;That's what I thought too.&lt;/p&gt;

&lt;p&gt;But while experimenting with React, I noticed something weird. So instead of guessing what's happening, let's break some HTML and see where it takes us.&lt;/p&gt;

&lt;p&gt;And before looking at the results, try to guess what will happen yourself.&lt;/p&gt;

&lt;h2&gt;
  
  
  Let's Break Some HTML
&lt;/h2&gt;

&lt;p&gt;Take 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;p&amp;gt;&lt;/span&gt;
  Hello
  &lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;World&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&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;Nothing complicated.&lt;/p&gt;

&lt;p&gt;Except it's invalid HTML.&lt;/p&gt;

&lt;p&gt;A &lt;code&gt;&amp;lt;p&amp;gt;&lt;/code&gt; element can only contain &lt;strong&gt;phrasing content&lt;/strong&gt;, so a &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; can't live inside it.&lt;/p&gt;

&lt;p&gt;Here's the first question:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What do you think the DOM will look like?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You might expect something 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;p
├── "Hello"
└── div
    └── "World"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But open DevTools and inspect it.&lt;/p&gt;

&lt;p&gt;You'll get something closer to:&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;p&amp;gt;&lt;/span&gt;Hello&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;World&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;p&amp;gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; is no longer inside the paragraph.&lt;/p&gt;

&lt;p&gt;What happened?&lt;/p&gt;

&lt;p&gt;When the HTML parser encounters the &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt;, its parsing rules close the open &lt;code&gt;&amp;lt;p&amp;gt;&lt;/code&gt; first. Later, when it reaches our original closing &lt;code&gt;&amp;lt;/p&amp;gt;&lt;/code&gt;, it has to deal with that too, which is why we can end up with that strange empty paragraph.&lt;/p&gt;

&lt;p&gt;Okay.&lt;/p&gt;

&lt;p&gt;We gave the browser invalid HTML, and the parser repaired it.&lt;/p&gt;

&lt;p&gt;Fair enough.&lt;/p&gt;

&lt;p&gt;Now let's annoy React with the same thing.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;App&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="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      Hello
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;World&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&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;p&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;p&gt;React warns us about the invalid nesting.&lt;/p&gt;

&lt;p&gt;But here's where I originally got confused.&lt;/p&gt;

&lt;p&gt;During client rendering, the DOM can still end up with that structure: the &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; inside the &lt;code&gt;&amp;lt;p&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Wait.&lt;/p&gt;

&lt;p&gt;Didn't the browser just "fix" the exact same structure?&lt;/p&gt;

&lt;p&gt;Why did one get repaired while the other didn't?&lt;/p&gt;

&lt;p&gt;My first explanation was:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;React handles HTML differently from the browser.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Not a terrible guess.&lt;/p&gt;

&lt;p&gt;Also not quite right.&lt;/p&gt;

&lt;h2&gt;
  
  
  Maybe React Isn't the Interesting Part
&lt;/h2&gt;

&lt;p&gt;Let's remove React completely.&lt;/p&gt;

&lt;p&gt;Try this:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;p&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;div&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;div&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Inspect the DOM again.&lt;/p&gt;

&lt;p&gt;We just constructed a &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; inside a &lt;code&gt;&amp;lt;p&amp;gt;&lt;/code&gt; using plain JavaScript.&lt;/p&gt;

&lt;p&gt;So...&lt;/p&gt;

&lt;p&gt;React wasn't the important part after all.&lt;/p&gt;

&lt;p&gt;The real difference is &lt;strong&gt;how we reached the DOM&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;When the browser receives:&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;p&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&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;it has HTML text that needs to be parsed.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;HTML
  ↓
HTML parser
  ↓
DOM
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The HTML parser has a surprisingly large set of rules for turning that text into a DOM tree, including rules for recovering from invalid markup.&lt;/p&gt;

&lt;p&gt;But &lt;code&gt;document.createElement()&lt;/code&gt; takes another path.&lt;/p&gt;

&lt;p&gt;We're explicitly creating nodes and connecting them ourselves:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;JavaScript
  ↓
DOM APIs
  ↓
DOM
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There's no &lt;code&gt;&amp;lt;p&amp;gt;&amp;lt;div&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/p&amp;gt;&lt;/code&gt; HTML string going through the HTML parser in that example.&lt;/p&gt;

&lt;p&gt;That's the part I was missing.&lt;/p&gt;

&lt;p&gt;And it gives us a better way to think about what we saw with React.&lt;/p&gt;

&lt;p&gt;JSX may look a lot like HTML, but during normal client rendering React isn't simply handing that JSX to the browser as an HTML document and asking the HTML parser to process it.&lt;/p&gt;

&lt;p&gt;So two pieces of code can look almost identical while taking different paths to the DOM.&lt;/p&gt;

&lt;h2&gt;
  
  
  Okay, Let's Make It Weirder
&lt;/h2&gt;

&lt;p&gt;Once I understood that, obviously I had to try more broken HTML.&lt;/p&gt;

&lt;p&gt;Take 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;image&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"cat.png"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Quick guess.&lt;/p&gt;

&lt;p&gt;What element do you think you'll find in the DOM?&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&amp;lt;image&amp;gt;&lt;/code&gt;?&lt;/p&gt;

&lt;p&gt;Nope.&lt;/p&gt;

&lt;p&gt;The HTML parser has a wonderfully specific rule for this.&lt;/p&gt;

&lt;p&gt;When it encounters an &lt;code&gt;image&lt;/code&gt; start tag, it treats it as a parse error, changes the tag name to &lt;code&gt;img&lt;/code&gt;, and processes it again.&lt;/p&gt;

&lt;p&gt;So we end up with:&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;img&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"cat.png"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Yes, this behavior is actually in the HTML specification.&lt;/p&gt;

&lt;p&gt;Now compare that with:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;element&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;image&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;createElement()&lt;/code&gt; creates the element we requested. Because &lt;code&gt;image&lt;/code&gt; isn't a recognized HTML element name, browsers represent it as an &lt;code&gt;HTMLUnknownElement&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Again, we're not asking the HTML parser to interpret an &lt;code&gt;&amp;lt;image&amp;gt;&lt;/code&gt; token.&lt;/p&gt;

&lt;p&gt;We're constructing an element directly.&lt;/p&gt;

&lt;p&gt;And suddenly what looked like:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"React is ignoring HTML rules."&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;looks more like:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"I was comparing HTML parsing with programmatic DOM construction."&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Much better question.&lt;/p&gt;

&lt;h2&gt;
  
  
  Then SSR Enters the Room
&lt;/h2&gt;

&lt;p&gt;At this point you could say:&lt;/p&gt;

&lt;p&gt;"Cool. Weird browser trivia. I'll forget it tomorrow."&lt;/p&gt;

&lt;p&gt;Fair.&lt;/p&gt;

&lt;p&gt;But there's one place where this becomes very practical for React developers:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Server-side rendering.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;With SSR, React produces HTML on the server.&lt;/p&gt;

&lt;p&gt;That HTML reaches the browser.&lt;/p&gt;

&lt;p&gt;Which means...&lt;/p&gt;

&lt;p&gt;Yep.&lt;/p&gt;

&lt;p&gt;We're back to the HTML parser.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;React on the server
      ↓
     HTML
      ↓
 HTML parser
      ↓
     DOM
      ↓
React hydration
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now imagine the server sends invalid nesting.&lt;/p&gt;

&lt;p&gt;Before hydration starts, the browser parses that HTML. If the parser repairs the structure, React can find a DOM tree that doesn't match what it expected to hydrate.&lt;/p&gt;

&lt;p&gt;Hello, hydration mismatch.&lt;/p&gt;

&lt;p&gt;React itself lists invalid HTML tag nesting as one possible cause of hydration failures.&lt;/p&gt;

&lt;p&gt;So those invalid nesting warnings aren't React being dramatic for no reason.&lt;/p&gt;

&lt;p&gt;Something that seems harmless while you're experimenting with client rendering can become a real problem once server-rendered HTML and hydration enter the picture.&lt;/p&gt;

&lt;h2&gt;
  
  
  So, Does React Break HTML Rules?
&lt;/h2&gt;

&lt;p&gt;Not really.&lt;/p&gt;

&lt;p&gt;My original question was slightly wrong.&lt;/p&gt;

&lt;p&gt;I was treating "the browser" as if it were one single mechanism.&lt;/p&gt;

&lt;p&gt;It isn't.&lt;/p&gt;

&lt;p&gt;HTML text goes through the HTML parser, which has rules for building a DOM tree and recovering from invalid markup.&lt;/p&gt;

&lt;p&gt;JavaScript can construct DOM nodes programmatically without sending the same HTML text through that parser.&lt;/p&gt;

&lt;p&gt;And React's client rendering doesn't follow the same path as parsing a server-delivered HTML document.&lt;/p&gt;

&lt;p&gt;With SSR, however, HTML parsing comes back into the picture.&lt;/p&gt;

&lt;p&gt;So the next time the browser mysteriously "fixes" your HTML or React starts complaining about hydration, there's a useful question to ask:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Who built this DOM: the HTML parser, or JavaScript?&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>react</category>
      <category>html</category>
      <category>browser</category>
    </item>
    <item>
      <title>Oxlint vs ESLint: Which One Should You Use?</title>
      <dc:creator>Ayman Eldawy</dc:creator>
      <pubDate>Tue, 21 Jul 2026 08:25:14 +0000</pubDate>
      <link>https://dev.to/aymaneldawy/oxlint-vs-eslint-which-one-should-you-use-cfc</link>
      <guid>https://dev.to/aymaneldawy/oxlint-vs-eslint-which-one-should-you-use-cfc</guid>
      <description>&lt;p&gt;If you've worked with JavaScript or TypeScript, you've probably used &lt;strong&gt;ESLint&lt;/strong&gt; at some point.&lt;/p&gt;

&lt;p&gt;But recently, a new competitor has been gaining attention: &lt;strong&gt;Oxlint&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;So, what's the difference, and which one should you choose?&lt;/p&gt;

&lt;p&gt;Let's start with the basics.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is a Linter?
&lt;/h2&gt;

&lt;p&gt;A &lt;strong&gt;linter&lt;/strong&gt; is a tool that analyzes your code and reports potential issues before they reach production.&lt;/p&gt;

&lt;p&gt;Beyond catching bugs, it also helps teams maintain a consistent coding style by enforcing shared rules across the codebase.&lt;/p&gt;

&lt;p&gt;In short, it keeps your code cleaner, more predictable, and easier to maintain.&lt;/p&gt;




&lt;h2&gt;
  
  
  ESLint
&lt;/h2&gt;

&lt;p&gt;ESLint has been the standard linter for the JavaScript ecosystem for years.&lt;/p&gt;

&lt;p&gt;If you've built a React, Next.js, Vue, or Node.js application, chances are you've already worked with it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pros
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;A massive ecosystem.&lt;/li&gt;
&lt;li&gt;Thousands of plugins and shareable configurations.&lt;/li&gt;
&lt;li&gt;Excellent support for React, Next.js, Vue, TypeScript, testing libraries, and more.&lt;/li&gt;
&lt;li&gt;Easy to write custom rules.&lt;/li&gt;
&lt;li&gt;Mature and battle-tested.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Cons
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Performance can become an issue in large projects.&lt;/li&gt;
&lt;li&gt;Every additional plugin increases linting time.&lt;/li&gt;
&lt;li&gt;Configuration can become complex as projects grow.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Oxlint
&lt;/h2&gt;

&lt;p&gt;Oxlint is a modern linter written in &lt;strong&gt;Rust&lt;/strong&gt;, designed from the ground up with one goal: &lt;strong&gt;speed&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;It's quickly becoming popular because of its impressive performance.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pros
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Often &lt;strong&gt;tens of times faster&lt;/strong&gt; than ESLint.&lt;/li&gt;
&lt;li&gt;Lower CPU and memory usage.&lt;/li&gt;
&lt;li&gt;Simple setup with an excellent developer experience.&lt;/li&gt;
&lt;li&gt;Great choice for large repositories and CI/CD pipelines.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Cons
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;The ecosystem is still much smaller than ESLint's.&lt;/li&gt;
&lt;li&gt;Not every ESLint rule or plugin is supported yet.&lt;/li&gt;
&lt;li&gt;Projects that rely heavily on custom ESLint plugins may still need ESLint.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Which one should you use?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Choose &lt;strong&gt;ESLint&lt;/strong&gt; if:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;You're working on an existing project.&lt;/li&gt;
&lt;li&gt;Your project depends on many ESLint plugins.&lt;/li&gt;
&lt;li&gt;You need custom linting rules.&lt;/li&gt;
&lt;li&gt;You're using frameworks like Next.js with established ESLint configurations.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Choose &lt;strong&gt;Oxlint&lt;/strong&gt; if:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;You're starting a new project.&lt;/li&gt;
&lt;li&gt;Performance is a top priority.&lt;/li&gt;
&lt;li&gt;Your project doesn't rely on many ESLint plugins.&lt;/li&gt;
&lt;li&gt;You want to significantly reduce linting time in CI.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Why not use both?
&lt;/h2&gt;

&lt;p&gt;You don't necessarily have to choose one over the other.&lt;/p&gt;

&lt;p&gt;Many teams combine them to get the best of both worlds.&lt;/p&gt;

&lt;p&gt;A common workflow looks like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use &lt;strong&gt;Oxlint&lt;/strong&gt; during development for extremely fast feedback.&lt;/li&gt;
&lt;li&gt;Run &lt;strong&gt;ESLint&lt;/strong&gt; before commits or in CI to enforce all project-specific rules and plugins.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This approach provides a great developer experience without sacrificing the flexibility of ESLint.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final thoughts
&lt;/h2&gt;

&lt;p&gt;There's no universal winner.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;ESLint&lt;/strong&gt; wins when you need flexibility, a mature ecosystem, and extensive plugin support.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Oxlint&lt;/strong&gt; wins when speed and developer experience are your highest priorities.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For many modern teams, using &lt;strong&gt;both&lt;/strong&gt; is becoming an increasingly popular choice.&lt;/p&gt;




&lt;p&gt;This is the first post in my new &lt;strong&gt;Frontend Battles&lt;/strong&gt; series, where I'll compare popular frontend tools, libraries, and technologies to help you understand their strengths, trade-offs, and when to choose each one.&lt;/p&gt;

</description>
      <category>frontend</category>
      <category>javascript</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Where Should You Store Your JWT?</title>
      <dc:creator>Ayman Eldawy</dc:creator>
      <pubDate>Wed, 15 Jul 2026 07:19:14 +0000</pubDate>
      <link>https://dev.to/aymaneldawy/where-should-you-store-your-jwt-19ad</link>
      <guid>https://dev.to/aymaneldawy/where-should-you-store-your-jwt-19ad</guid>
      <description>&lt;p&gt;One of the first questions developers face when implementing an authentication flow is:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where should I store the JWT?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;After all, your application needs easy access to the token so it can send it in the &lt;code&gt;Authorization&lt;/code&gt; header when making authenticated requests.&lt;/p&gt;

&lt;p&gt;The two most common options are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Local Storage&lt;/li&gt;
&lt;li&gt;HttpOnly Cookies&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's compare them.&lt;/p&gt;




&lt;h2&gt;
  
  
  Option 1: Local Storage
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;localStorage&lt;/code&gt; is a browser storage API that allows JavaScript to store and retrieve 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 javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;token&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;jwt&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The biggest advantage is convenience.&lt;/p&gt;

&lt;p&gt;Your token is easy to access from anywhere in your frontend application.&lt;/p&gt;

&lt;p&gt;However, that convenience comes with an important security trade-off.&lt;/p&gt;

&lt;p&gt;Since JavaScript has full access to Local Storage, &lt;strong&gt;any JavaScript running on your page can also read your token.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If your application is vulnerable to &lt;strong&gt;Cross-Site Scripting (XSS)&lt;/strong&gt; and an attacker manages to inject malicious JavaScript, they can simply read the token and send it to their own server.&lt;/p&gt;

&lt;p&gt;That's one of the main reasons security professionals discourage storing JWTs in Local Storage.&lt;/p&gt;

&lt;p&gt;But security isn't the only drawback.&lt;/p&gt;

&lt;p&gt;Because Local Storage only exists in the browser, it isn't available during server-side rendering.&lt;/p&gt;

&lt;p&gt;That means if your authentication relies on a token stored in Local Storage, your authenticated requests typically have to be made from the client.&lt;/p&gt;

&lt;p&gt;For frameworks like &lt;strong&gt;Next.js&lt;/strong&gt;, this means giving up many of the benefits of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Server Components&lt;/li&gt;
&lt;li&gt;Server-side rendering (SSR)&lt;/li&gt;
&lt;li&gt;Server-side data fetching&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Simply put, the server cannot access Local Storage.&lt;/p&gt;




&lt;h2&gt;
  
  
  Option 2: HttpOnly Cookies
&lt;/h2&gt;

&lt;p&gt;A much more secure option is storing the token inside an &lt;strong&gt;HttpOnly Cookie&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The key advantage is that JavaScript &lt;strong&gt;cannot read HttpOnly cookies.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Even if an attacker successfully exploits an XSS vulnerability, they still can't directly access the token stored inside the cookie.&lt;/p&gt;

&lt;p&gt;This provides a significant security improvement.&lt;/p&gt;

&lt;p&gt;Another major benefit is that cookies are automatically included with requests to your server.&lt;/p&gt;

&lt;p&gt;That means the token is available on both:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The client&lt;/li&gt;
&lt;li&gt;The server&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is especially valuable in frameworks like &lt;strong&gt;Next.js&lt;/strong&gt;, where Server Components and SSR often need access to the authenticated user's session.&lt;/p&gt;




&lt;h2&gt;
  
  
  Is HttpOnly Cookie always better?
&lt;/h2&gt;

&lt;p&gt;Not necessarily.&lt;/p&gt;

&lt;p&gt;Like most engineering decisions, it's a trade-off.&lt;/p&gt;

&lt;h3&gt;
  
  
  Local Storage
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Pros&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Very simple to implement&lt;/li&gt;
&lt;li&gt;Easy to access from JavaScript&lt;/li&gt;
&lt;li&gt;Works well for purely client-side applications&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cons&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Vulnerable to XSS attacks&lt;/li&gt;
&lt;li&gt;Not available during server-side rendering&lt;/li&gt;
&lt;li&gt;Forces authentication logic to stay on the client&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  HttpOnly Cookies
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Pros&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Much more resistant to XSS attacks&lt;/li&gt;
&lt;li&gt;Available to both the client and the server&lt;/li&gt;
&lt;li&gt;Works naturally with SSR and Server Components&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cons&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;More complex to implement&lt;/li&gt;
&lt;li&gt;Requires proper cookie configuration (&lt;code&gt;HttpOnly&lt;/code&gt;, &lt;code&gt;Secure&lt;/code&gt;, &lt;code&gt;SameSite&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;You also need to consider CSRF protection, since cookies are automatically sent with requests&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  So... which one should you choose?
&lt;/h2&gt;

&lt;p&gt;For modern web applications—especially those built with frameworks like &lt;strong&gt;Next.js&lt;/strong&gt;—&lt;strong&gt;HttpOnly Cookies are generally the recommended approach.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;They provide stronger security, work seamlessly with server-side rendering, and align better with modern authentication architectures.&lt;/p&gt;

&lt;p&gt;Local Storage is easier to use, but that convenience comes with meaningful security and architectural limitations.&lt;/p&gt;




&lt;p&gt;This is the third post in my &lt;strong&gt;Frontend Security&lt;/strong&gt; series.&lt;/p&gt;

&lt;p&gt;Follow me to stay updated with the next episodes of my Frontend Security series&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>security</category>
      <category>cookie</category>
      <category>frontend</category>
    </item>
    <item>
      <title>XSS Attacks</title>
      <dc:creator>Ayman Eldawy</dc:creator>
      <pubDate>Mon, 06 Jul 2026 07:16:42 +0000</pubDate>
      <link>https://dev.to/aymaneldawy/understanding-the-different-types-of-xss-attacks-39ih</link>
      <guid>https://dev.to/aymaneldawy/understanding-the-different-types-of-xss-attacks-39ih</guid>
      <description>&lt;p&gt;You’ve probably heard about &lt;strong&gt;Cross-Site Scripting (XSS)&lt;/strong&gt; before, but did you know that it comes in multiple forms?&lt;/p&gt;

&lt;p&gt;Each type targets a different part of your application, and understanding the differences is essential for building secure frontend applications.&lt;/p&gt;

&lt;p&gt;Let's start with the basics.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is XSS?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Cross-Site Scripting (XSS)&lt;/strong&gt; is a security vulnerability that allows an attacker to inject malicious code, typically JavaScript, into a web page.&lt;/p&gt;

&lt;p&gt;When that code is executed in another user's browser, the attacker may be able to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Steal cookies or authentication tokens&lt;/li&gt;
&lt;li&gt;Read sensitive information&lt;/li&gt;
&lt;li&gt;Modify the page content&lt;/li&gt;
&lt;li&gt;Perform actions on behalf of the user&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Most XSS vulnerabilities happen because applications don't safely handle user input.&lt;/p&gt;




&lt;h2&gt;
  
  
  The three main types of XSS
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Stored XSS&lt;/li&gt;
&lt;li&gt;Reflected XSS&lt;/li&gt;
&lt;li&gt;DOM-Based XSS&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let's look at each one.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Stored XSS
&lt;/h2&gt;

&lt;p&gt;This is generally considered the most dangerous type of XSS.&lt;/p&gt;

&lt;p&gt;It occurs when malicious user input is &lt;strong&gt;stored in the database&lt;/strong&gt; and later displayed to other users without proper sanitization or output encoding.&lt;/p&gt;

&lt;p&gt;Common places where this happens include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Comments&lt;/li&gt;
&lt;li&gt;Product reviews&lt;/li&gt;
&lt;li&gt;Forum posts&lt;/li&gt;
&lt;li&gt;User profiles&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Imagine an attacker submits the following comment:&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;script&amp;gt;&lt;/span&gt;
  &lt;span class="c1"&gt;// malicious JavaScript&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the application stores and renders that content without sanitizing it, &lt;strong&gt;every user who visits the page will execute the attacker's script.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That's why it's called &lt;strong&gt;"stored XSS";&lt;/strong&gt; the payload is permanently stored on the server.&lt;/p&gt;

&lt;h3&gt;
  
  
  Impact
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Large-scale attacks affecting many users&lt;/li&gt;
&lt;li&gt;Session hijacking&lt;/li&gt;
&lt;li&gt;Account compromise&lt;/li&gt;
&lt;li&gt;Data theft&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  How to prevent it
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Validate and sanitize user input.&lt;/li&gt;
&lt;li&gt;Apply proper output encoding before rendering content.&lt;/li&gt;
&lt;li&gt;Use trusted sanitization libraries such as &lt;strong&gt;DOMPurify&lt;/strong&gt; when rendering HTML.&lt;/li&gt;
&lt;li&gt;Enable &lt;strong&gt;Content Security Policy (CSP)&lt;/strong&gt; as an additional defense layer.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  2. Reflected XSS
&lt;/h2&gt;

&lt;p&gt;Unlike Stored XSS, the malicious payload is &lt;strong&gt;not stored&lt;/strong&gt; anywhere.&lt;/p&gt;

&lt;p&gt;Instead, it is reflected back to the user immediately through request data such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Search inputs&lt;/li&gt;
&lt;li&gt;Query parameters&lt;/li&gt;
&lt;li&gt;URL fragments&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A common attack looks like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The attacker crafts a malicious URL.&lt;/li&gt;
&lt;li&gt;The victim clicks the link.&lt;/li&gt;
&lt;li&gt;The application reflects the input back into the page without escaping it.&lt;/li&gt;
&lt;li&gt;The browser executes the injected JavaScript.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Because the payload only exists in that request, the victim must interact with the malicious link for the attack to work.&lt;/p&gt;

&lt;h3&gt;
  
  
  Impact
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Cookie or token theft&lt;/li&gt;
&lt;li&gt;Session hijacking&lt;/li&gt;
&lt;li&gt;Redirecting users to phishing websites&lt;/li&gt;
&lt;li&gt;Executing actions on behalf of the victim&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  How to prevent it
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Validate incoming input.&lt;/li&gt;
&lt;li&gt;Always apply proper output encoding.&lt;/li&gt;
&lt;li&gt;Never render raw user input directly into HTML.&lt;/li&gt;
&lt;li&gt;Enable &lt;strong&gt;Content Security Policy (CSP)&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  3. DOM-Based XSS
&lt;/h2&gt;

&lt;p&gt;This type is especially important for frontend developers because the vulnerability exists &lt;strong&gt;entirely inside the browser&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The server may never be involved.&lt;/p&gt;

&lt;p&gt;It happens when JavaScript reads data from an untrusted source and writes it directly into the DOM without proper sanitization.&lt;/p&gt;

&lt;p&gt;Common unsafe APIs include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;innerHTML&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;outerHTML&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;insertAdjacentHTML&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;document.write()&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you're using React, you're already safer because React automatically escapes text by default.&lt;/p&gt;

&lt;p&gt;However, that protection disappears when using APIs like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;dangerouslySetInnerHTML&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the HTML you're rendering comes from an untrusted source, you can easily introduce a DOM XSS vulnerability.&lt;/p&gt;

&lt;h3&gt;
  
  
  Impact
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Data theft&lt;/li&gt;
&lt;li&gt;Page manipulation&lt;/li&gt;
&lt;li&gt;Executing arbitrary JavaScript&lt;/li&gt;
&lt;li&gt;Session hijacking&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  How to prevent it
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Avoid using &lt;code&gt;innerHTML&lt;/code&gt; whenever possible.&lt;/li&gt;
&lt;li&gt;Avoid &lt;code&gt;dangerouslySetInnerHTML&lt;/code&gt; with untrusted content.&lt;/li&gt;
&lt;li&gt;Sanitize HTML before rendering it.&lt;/li&gt;
&lt;li&gt;Review dangerous DOM sinks in your application.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The golden rule
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Never trust user input.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Whether the data comes from a form, a URL, an API, or even your own database, treat it as untrusted until you've validated, sanitized, and safely rendered it.&lt;/p&gt;

&lt;p&gt;Defense against XSS isn't a single technique; it's a combination of secure coding practices, proper output encoding, sanitization, and browser protections like CSP.&lt;/p&gt;




&lt;p&gt;This is the second post in my &lt;strong&gt;Frontend Security&lt;/strong&gt; series.&lt;/p&gt;

&lt;p&gt;In the previous post, we explored &lt;strong&gt;Content Security Policy (CSP)&lt;/strong&gt; and how it helps mitigate XSS attacks.&lt;br&gt;
if you'd like to check it out.&lt;br&gt;
&lt;a href="https://dev.to/aymaneldawy/what-is-content-security-policy-csp-p6p"&gt;https://dev.to/aymaneldawy/what-is-content-security-policy-csp-p6p&lt;/a&gt;&lt;/p&gt;

</description>
      <category>frontend</category>
      <category>security</category>
      <category>javascript</category>
      <category>xss</category>
    </item>
    <item>
      <title>What is Content Security Policy (CSP)?</title>
      <dc:creator>Ayman Eldawy</dc:creator>
      <pubDate>Thu, 02 Jul 2026 07:43:56 +0000</pubDate>
      <link>https://dev.to/aymaneldawy/what-is-content-security-policy-csp-p6p</link>
      <guid>https://dev.to/aymaneldawy/what-is-content-security-policy-csp-p6p</guid>
      <description>&lt;p&gt;One of the most important, yet often underrated, topics in frontend security is &lt;strong&gt;Content Security Policy (CSP)&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;You can think of CSP as a set of instructions you give the browser, telling it &lt;strong&gt;what is allowed to run and what should be blocked&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Let's break it down.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is CSP?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Content Security Policy (CSP)&lt;/strong&gt; is an &lt;strong&gt;HTTP response header&lt;/strong&gt; that you configure on your server to tell the browser which sources are trusted for loading resources such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;JavaScript&lt;/li&gt;
&lt;li&gt;CSS&lt;/li&gt;
&lt;li&gt;Images&lt;/li&gt;
&lt;li&gt;Videos&lt;/li&gt;
&lt;li&gt;Fonts&lt;/li&gt;
&lt;li&gt;And other assets&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Imagine the browser has a &lt;strong&gt;whitelist&lt;/strong&gt; of trusted sources. Whenever it tries to load a resource, it checks whether that source is on the list.&lt;/p&gt;

&lt;p&gt;If the source isn't trusted, the browser simply blocks it.&lt;/p&gt;

&lt;p&gt;This makes it much harder for attackers to inject and execute malicious code on your website.&lt;/p&gt;




&lt;h2&gt;
  
  
  What problem does CSP solve?
&lt;/h2&gt;

&lt;p&gt;The primary problem CSP helps mitigate is &lt;strong&gt;Cross-Site Scripting (XSS)&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;An XSS attack allows an attacker to inject malicious JavaScript into your application. Once executed, that script could:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Steal cookies&lt;/li&gt;
&lt;li&gt;Read data from Local Storage&lt;/li&gt;
&lt;li&gt;Modify the page&lt;/li&gt;
&lt;li&gt;Perform actions on behalf of the user&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  How does CSP stop this?
&lt;/h3&gt;

&lt;p&gt;Even if an attacker successfully injects JavaScript into your page, the browser will first check your CSP policy.&lt;/p&gt;

&lt;p&gt;If that script doesn't come from an approved source, &lt;strong&gt;the browser refuses to execute it&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In many cases, the attack simply fails before any malicious code runs.&lt;/p&gt;




&lt;h2&gt;
  
  
  CSP helps with more than XSS
&lt;/h2&gt;

&lt;p&gt;Although CSP is mainly known for mitigating XSS attacks, it can also help reduce other security risks, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Clickjacking&lt;/li&gt;
&lt;li&gt;Loading untrusted third-party resources&lt;/li&gt;
&lt;li&gt;Restricting iframes&lt;/li&gt;
&lt;li&gt;Controlling redirects&lt;/li&gt;
&lt;li&gt;Restricting form submissions&lt;/li&gt;
&lt;li&gt;Controlling where images, fonts, stylesheets, and scripts can be loaded from&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Despite being "just an HTTP header," CSP offers a surprisingly powerful security layer.&lt;/p&gt;

&lt;p&gt;The downside?&lt;/p&gt;

&lt;p&gt;Implementing a strict CSP can be challenging because you'll often need to carefully define every trusted resource your application depends on.&lt;/p&gt;




&lt;h2&gt;
  
  
  A simple example
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;Content-Security-Policy:
  default-src 'self';
  script-src 'self' https://example.com;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What does this mean?
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;default-src 'self'&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;By default, resources should only be loaded from your own domain.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;script-src 'self' https://example.com&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;JavaScript is only allowed to execute if it comes from your own website or from &lt;code&gt;https://example.com&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Any script loaded from another source will be blocked by the browser.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;The stricter your policy becomes, the stronger your security.&lt;/p&gt;

&lt;p&gt;Of course, finding the right balance between security and usability can take some effort.&lt;/p&gt;

&lt;p&gt;If you're using &lt;strong&gt;Next.js&lt;/strong&gt;, you can follow this guide to show you how to configure CSP in your application.&lt;br&gt;
&lt;a href="https://nextjs.org/docs/app/guides/content-security-policy" rel="noopener noreferrer"&gt;https://nextjs.org/docs/app/guides/content-security-policy&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you're using &lt;strong&gt;Angular&lt;/strong&gt;, you'll also find official documentation covering CSP support.&lt;/p&gt;




&lt;p&gt;This is the first post in a series about &lt;strong&gt;Frontend Security&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Over the next few posts, we'll explore more real-world security topics that every frontend developer should understand.&lt;/p&gt;

</description>
      <category>csp</category>
      <category>security</category>
      <category>frontend</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
