<?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: Arsany</title>
    <description>The latest articles on DEV Community by Arsany (@101stand).</description>
    <link>https://dev.to/101stand</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%2F4207357%2Fcd86fadb-aa5b-4a5f-bb85-43f6973289d2.png</url>
      <title>DEV Community: Arsany</title>
      <link>https://dev.to/101stand</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/101stand"/>
    <language>en</language>
    <item>
      <title>Google saw 1 word on my Next.js homepage. A Suspense boundary was hiding everything else.</title>
      <dc:creator>Arsany</dc:creator>
      <pubDate>Mon, 26 Oct 2026 18:20:09 +0000</pubDate>
      <link>https://dev.to/101stand/google-saw-1-word-on-my-nextjs-homepage-a-suspense-boundary-was-hiding-everything-else-4ohe</link>
      <guid>https://dev.to/101stand/google-saw-1-word-on-my-nextjs-homepage-a-suspense-boundary-was-hiding-everything-else-4ohe</guid>
      <description>&lt;p&gt;Last night I ran an SEO audit on Ilytics, the analytics tool I've been building, and it came back with 0 words on every page.&lt;/p&gt;

&lt;p&gt;I assumed the audit tool was broken. The site looked fine in my browser. So I fetched the homepage the way a crawler does, with curl and a Googlebot user agent, and counted the words in the HTML. There was exactly one: "Ilytics", from the page title. No headings, no text, no links to my other pages.&lt;/p&gt;

&lt;p&gt;The fix ended up being about twenty lines. It's an easy mistake to make in the App Router, so I'm writing it up.&lt;/p&gt;

&lt;h2&gt;
  
  
  The provider that wrapped everything
&lt;/h2&gt;

&lt;p&gt;My root layout wraps the whole app in an analytics provider, which is how most PostHog setups for Next.js look:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app/layout.tsx&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;PostHogProvider&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;PostHogProvider&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Inside, the provider reads the URL so it can send a pageview whenever the path or query string changes. Because &lt;code&gt;useSearchParams()&lt;/code&gt; needs a Suspense boundary, I had wrapped it in one:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;use client&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;PostHogInner&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;children&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;pathname&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;usePathname&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;searchParams&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useSearchParams&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="c1"&gt;// ...init, identify, pageview tracking...&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;PHProvider&lt;/span&gt; &lt;span class="na"&gt;client&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;posthog&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;PHProvider&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;PostHogProvider&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;children&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;Suspense&lt;/span&gt; &lt;span class="na"&gt;fallback&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="si"&gt;}&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;PostHogInner&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;PostHogInner&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;Suspense&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;h2&gt;
  
  
  Why that empties the page
&lt;/h2&gt;

&lt;p&gt;When a statically rendered page calls &lt;code&gt;useSearchParams()&lt;/code&gt;, Next.js can't know the query string at build time. So everything up to the nearest Suspense boundary gets rendered in the browser instead of on the server, and the server HTML contains the boundary's fallback.&lt;/p&gt;

&lt;p&gt;My boundary wrapped &lt;code&gt;{children}&lt;/code&gt;, which in a root layout is the entire site. The fallback was &lt;code&gt;null&lt;/code&gt;. So every page was pre-rendered as nothing, and JavaScript filled it in after load.&lt;/p&gt;

&lt;p&gt;Visitors never noticed because it happens in a fraction of a second. Crawlers that read the server HTML got an empty page.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fix is to shrink the boundary
&lt;/h2&gt;

&lt;p&gt;Only the part that reads the query string needs to wait for the browser. I moved pageview tracking into its own component, gave it its own Suspense boundary, and rendered &lt;code&gt;children&lt;/code&gt; outside of it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;use client&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;PageviewTracker&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;pathname&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;usePathname&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;searchParams&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useSearchParams&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="nf"&gt;useEffect&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="nf"&gt;ensurePostHog&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;posthog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;capture&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;$pageview&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="na"&gt;$current_url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;href&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="nx"&gt;pathname&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="k"&gt;return&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="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;PostHogProvider&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// session and identify logic stays here, without useSearchParams&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;PHProvider&lt;/span&gt; &lt;span class="na"&gt;client&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;posthog&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Suspense&lt;/span&gt; &lt;span class="na"&gt;fallback&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="si"&gt;}&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;PageviewTracker&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;Suspense&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;PHProvider&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;The boundary now wraps a component that renders &lt;code&gt;null&lt;/code&gt; anyway, so nothing is lost, and the actual pages render on the server again.&lt;/p&gt;

&lt;p&gt;One thing broke along the way. React runs a child component's effects before its parent's, so the tracker started calling &lt;code&gt;capture()&lt;/code&gt; before the parent had called &lt;code&gt;posthog.init()&lt;/code&gt;. I moved the init into a small &lt;code&gt;ensurePostHog()&lt;/code&gt; function that both components call, with a flag so it only runs once.&lt;/p&gt;

&lt;h2&gt;
  
  
  Before and after
&lt;/h2&gt;

&lt;p&gt;I fetched the same pages as Googlebot before and after deploying. Visible words in the HTML:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Home: 1 before, 375 after&lt;/li&gt;
&lt;li&gt;Pricing: 0 before, 211 after&lt;/li&gt;
&lt;li&gt;FAQ: 0 before, 159 after&lt;/li&gt;
&lt;li&gt;Docs: 0 before, 138 after&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each page also went from zero to one H1, and from no internal links in the HTML to between 6 and 15.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to check your own site
&lt;/h2&gt;

&lt;p&gt;Run this against any page and compare the number with what you see in the browser:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-A&lt;/span&gt; &lt;span class="s2"&gt;"Googlebot"&lt;/span&gt; https://your-site.com/ &lt;span class="se"&gt;\&lt;/span&gt;
  | &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-E&lt;/span&gt; &lt;span class="s1"&gt;'s/&amp;lt;script[^&amp;gt;]*&amp;gt;.*&amp;lt;\/script&amp;gt;//g; s/&amp;lt;[^&amp;gt;]+&amp;gt;/ /g'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  | &lt;span class="nb"&gt;wc&lt;/span&gt; &lt;span class="nt"&gt;-w&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If it's close to zero, look for &lt;code&gt;useSearchParams()&lt;/code&gt; or other client-only hooks inside a Suspense boundary that sits high up in your layout.&lt;/p&gt;

&lt;p&gt;Google does render JavaScript eventually, but it's slower and less reliable than reading HTML, and many other crawlers don't run JavaScript at all. I'd rather not depend on it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd check next time
&lt;/h2&gt;

&lt;p&gt;If you add any tracking or auth provider to a Next.js layout, check where its Suspense boundary sits before you ship. The Next.js docs on &lt;a href="https://nextjs.org/docs/app/api-reference/functions/use-search-params" rel="noopener noreferrer"&gt;&lt;code&gt;useSearchParams&lt;/code&gt;&lt;/a&gt; explain the client-side rendering behavior in more detail.&lt;/p&gt;

&lt;p&gt;It's a little embarrassing that the thing hiding my analytics product from search was an analytics provider. At least it made for a good debugging session.&lt;/p&gt;

&lt;p&gt;Arsany&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>webdev</category>
      <category>seo</category>
      <category>react</category>
    </item>
    <item>
      <title>The EU Just Moved to Make Website Analytics Consent-Free. Here's the Catch.</title>
      <dc:creator>Arsany</dc:creator>
      <pubDate>Sun, 25 Oct 2026 22:05:28 +0000</pubDate>
      <link>https://dev.to/101stand/the-eu-just-moved-to-make-website-analytics-consent-free-heres-the-catch-21n1</link>
      <guid>https://dev.to/101stand/the-eu-just-moved-to-make-website-analytics-consent-free-heres-the-catch-21n1</guid>
      <description>&lt;p&gt;On October 21, 2026, EU ambassadors agreed the Council's position on the Digital Omnibus, a package that amends the GDPR and the ePrivacy rules. Buried in its 179 pages is a line every website owner should read: anonymous, first-party audience measurement could run without asking for consent.&lt;/p&gt;

&lt;p&gt;In other words, simple analytics may no longer need a cookie banner. But there is a catch, and a long timeline.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the Council agreed
&lt;/h2&gt;

&lt;p&gt;The Council kept cookie rules where they are today, in the ePrivacy Directive. Consent stays the general rule. What changes is the list of exceptions: device access without consent would be allowed only when it is strictly necessary for one of six purposes.&lt;/p&gt;

&lt;p&gt;One of those purposes is anonymous, aggregated audience measurement of your own service. It can be run by you or by a provider on your behalf, as long as the data is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;anonymous and aggregated&lt;/li&gt;
&lt;li&gt;used only to measure your own service&lt;/li&gt;
&lt;li&gt;not shared with third parties&lt;/li&gt;
&lt;li&gt;not combined with third-party data&lt;/li&gt;
&lt;li&gt;not reused for any other purpose&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The mandate also requires a one-click way to refuse cookies, and bars sites from asking again for at least four months after a refusal. The Commission had proposed six.&lt;/p&gt;

&lt;h2&gt;
  
  
  What still needs consent
&lt;/h2&gt;

&lt;p&gt;The exemption is narrow on purpose. Consent is still required for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;cross-site tracking&lt;/li&gt;
&lt;li&gt;advertising profiles&lt;/li&gt;
&lt;li&gt;analytics that merges your stats with outside data&lt;/li&gt;
&lt;li&gt;anything that is not anonymous and aggregated&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So the cookie banner is not dead. It is simply no longer meant for basic "how many people visited" analytics.&lt;/p&gt;

&lt;h2&gt;
  
  
  When it would actually apply
&lt;/h2&gt;

&lt;p&gt;Nothing changes today. Here is the path ahead:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Step&lt;/th&gt;
&lt;th&gt;Status&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Council mandate agreed by Coreper&lt;/td&gt;
&lt;td&gt;Done, Oct 21, 2026&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;European Parliament adopts its position&lt;/td&gt;
&lt;td&gt;Not yet, as of Oct 24, 2026&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Council and Parliament negotiate the final text&lt;/td&gt;
&lt;td&gt;Starts after Parliament's position&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Member states transpose the cookie changes&lt;/td&gt;
&lt;td&gt;24 months after adoption&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The EU wants its simplification packages done by the end of 2027, so legal analysts expect the cookie changes to take effect around 2028 at the earliest. Until then, current GDPR and ePrivacy rules apply in full.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why it matters anyway
&lt;/h2&gt;

&lt;p&gt;Even before it becomes law, the direction is clear. Europe is drawing a line between analytics that measures your own site and analytics that tracks people. The first is being made easier. The second keeps its friction.&lt;/p&gt;

&lt;p&gt;For developers and founders, that means the less your analytics collects, the less it will cost you, both in legal work and in the visitors you lose to consent pop-ups.&lt;/p&gt;

&lt;p&gt;I'm building Ilytics, a small analytics tool, around the same idea, so I'll be watching how Parliament's position turns out.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This article summarizes public reporting on a draft legislative position. It is not legal advice.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://news.europawire.eu/council-of-the-eu-backs-digital-omnibus-changes-to-gdpr-cookie-consent-and-cyber-incident-reporting/eu-press-release/2026/10/22/12/17/03/183831/" rel="noopener noreferrer"&gt;Council of the EU backs Digital Omnibus changes (EU press release via EuropaWire)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://ppc.land/eu-council-agrees-4-month-cookie-re-ask-wait-for-digital-omnibus-talks/" rel="noopener noreferrer"&gt;EU Council agrees 4-month cookie re-ask wait (PPC Land)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://legal247.no/en/insights/digital-omnibus-gdpr/" rel="noopener noreferrer"&gt;Digital Omnibus and the GDPR: the Council mandate (Legal247)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>privacy</category>
      <category>webdev</category>
      <category>gdpr</category>
      <category>analytics</category>
    </item>
  </channel>
</rss>
