<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Javascript &amp; Render - Medium]]></title>
        <description><![CDATA[Your go-to publication for the latest in front-end development. From cutting-edge JavaScript frameworks to elegant UI/UX design principles, we bring insightful articles, expert opinions, and hands-on tutorials to help developers craft seamless digital experiences. - Medium]]></description>
        <link>https://medium.com/javascript-render?source=rss----da9975776266---4</link>
        <image>
            <url>https://cdn-images-1.medium.com/proxy/1*TGH72Nnw24QL3iV9IOm4VA.png</url>
            <title>Javascript &amp;amp; Render - Medium</title>
            <link>https://medium.com/javascript-render?source=rss----da9975776266---4</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Fri, 13 Nov 2026 23:19:20 GMT</lastBuildDate>
        <atom:link href="https://medium.com/feed/javascript-render" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com/" rel="hub"/>
        <item>
            <title><![CDATA[Branded Types & Zod: The Senior Engineer’s Secret to Safety]]></title>
            <link>https://medium.com/javascript-render/branded-types-zod-the-senior-engineers-secret-to-safety-2e4a55438ae3?source=rss----da9975776266---4</link>
            <guid isPermaLink="false">https://medium.com/p/2e4a55438ae3</guid>
            <category><![CDATA[branded-types]]></category>
            <category><![CDATA[type-safety]]></category>
            <category><![CDATA[typescript]]></category>
            <dc:creator><![CDATA[Gerardo Perrucci]]></dc:creator>
            <pubDate>Mon, 15 Dec 2025 09:16:09 GMT</pubDate>
            <atom:updated>2025-12-15T09:16:09.681Z</atom:updated>
            <content:encoded><![CDATA[<p>In over twelve years of building distributed systems, ranging from high-frequency trading platforms to massive gaming portals, I have found that the most dangerous bugs are rarely the complex ones. They are the boring ones.</p><p><strong>Original post on my personal blog. </strong><a href="https://www.gperrucci.com/en/blog/typescript/branded-types-zod-senior-engineer-secret-safety"><strong>Click here to read the full article</strong></a></p><blockquote>The most insidious bug in a mature codebase is <strong>Primitive Obsession</strong>.</blockquote><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*tNErspHtgHpYR8S0tSgHtQ.png" /></figure><p>We define our complex worlds in terms of string, number, and boolean. To our compilers, a UserId is mathematically identical to a TransactionId. A StockPrice is just a number, exactly the same as a Temperature.</p><p>This leads to the classic “swapped argument” disaster:</p><p>TypeScript</p><pre>// The compiler thinks this is perfectly fine.<br>// But you just sent money to the wrong person.<br>transfer(amount, toAddress, fromAddress);</pre><p>If your function signature is transfer(amount: number, from: string, to: string), you have effectively disabled the type system for those arguments. You are flying blind.</p><p>In this post, I am going to show you how to fix this using <strong>Branded Types</strong> and <strong>Zod</strong>. We are going to move from “trusting the developer” to “enforcing the architecture.”</p><h3>The Problem: TypeScript is Structural</h3><p>To solve this, we have to fight TypeScript’s nature. TypeScript uses a <strong>structural</strong> type system. If it looks like a duck, the compiler accepts it as a duck.</p><pre>interface Admin { id: string; }<br>interface User { id: string; }<br>const admin: Admin = { id: &quot;admin_1&quot; };<br>const user: User = admin; // ✅ Valid in TypeScript!</pre><p>This is great for flexibility but terrible for domain modeling. We need <strong>Nominal Typing</strong>. We need a system where a User is distinct from an Admin because we <em>said so</em>, not just because they happen to have the same fields.</p><p>Since TypeScript does not support this natively, we have to lie to the compiler. It is a useful lie.</p><h3>The Solution: The “Unique Symbol” Brand</h3><p>I have seen many ways to implement Branded types, including classes and private fields. Most have runtime overhead or clutter your IntelliSense.</p><p>After years of iteration, this is the <strong>only pattern I recommend for production</strong>. It uses unique symbol to create a &quot;private&quot; property that exists only in the type system.</p><pre>// branding.ts<br>// 1. Declare a unique symbol.<br>// This exists only in type space if we don&#39;t export the value.<br>declare const __brand: unique symbol;<br>// 2. Define the generic utility<br>export type Brand&lt;T, B&gt; = T &amp; { [__brand]: B };<br>// 3. Define your domain types<br>export type UserId = Brand&lt;string, &quot;UserId&quot;&gt;;<br>export type Email = Brand&lt;string, &quot;Email&quot;&gt;;<br>export type Cents = Brand&lt;number, &quot;Cents&quot;&gt;;</pre><p>This approach wins for three reasons. First, it has <strong>Zero Runtime Cost</strong> because the intersection vanishes after compilation. Second, it offers maximum safety because __brand is a unique symbol, so no other type can accidentally match it. Third, it keeps your runtime objects clean without ugly _type properties cluttering your console logs.</p><p>Now, if you try to assign a plain string to a UserId, TypeScript screams at you.</p><p>TypeScript</p><pre>const id: UserId = &quot;123&quot;; <br>// ❌ Error: Type &#39;string&#39; is not assignable to type &#39;UserId&#39;</pre><h3>The Runtime Gatekeeper: Integrating Zod</h3><p>This creates a new problem. If we cannot assign a string to a UserId, how do we ever create one?</p><p>You <em>could</em> manually cast it using as UserId, but that defeats the purpose. Manual casting is dangerous. We need a rigorous way to &quot;bless&quot; raw data into our trusted types.</p><p>This is where <strong>Zod</strong> shines. We use Zod to validate the <em>structure</em> at the I/O boundary. If it passes validation, we transform it into our <em>brand</em>.</p><h3>The “Parse, Don’t Validate” Pattern</h3><p>I rarely use Zod’s native .brand() method because it locks you into Zod&#39;s internal types. Instead, I use a custom transform pipeline.</p><p>TypeScript</p><pre>import { z } from &quot;zod&quot;;<br>import { UserId, Email } from &quot;./types&quot;;<br>// The Gatekeeper<br>export const UserIdSchema = z.string()<br>.uuid() // 1. Validate structure first<br>.transform((val) =&gt; val as UserId); // 2. &quot;Bless&quot; the type<br><br>export const EmailSchema = z.string()<br>.email()<br>.toLowerCase() // Normalization is free here!<br>.transform((val) =&gt; val as Email);</pre><p>Why is as UserId safe here?</p><p>It is safe because it lives inside the Zod pipeline. We only cast the value after Zod has verified it is a valid UUID. We have encapsulated the “unsafe” operation inside a verified factory.</p><h3>Putting It Together: The Architecture</h3><p>Here is how this looks in a real Next.js or Node.js application.</p><h3>1. The Domain Layer (Core)</h3><p>Your business logic becomes self-documenting. It is impossible to call this function with an unvalidated string.</p><p>TypeScript</p><pre>// domain/users.ts<br>import { UserId, Email } from &quot;@/types&quot;;<br><br>export async function activateUser(id: UserId, email: Email) {<br>  // If we are here, &#39;id&#39; is guaranteed to be a valid UUID<br>  // and &#39;email&#39; is guaranteed to be a valid, lowercase email.<br>  console.log(`Activating ${id}...`);<br>}</pre><h3>2. The Input Layer (API/Actions)</h3><p>This is the <strong>only</strong> place where raw strings exist.</p><p>TypeScript</p><pre>// actions.ts<br>import { UserIdSchema, EmailSchema } from &quot;@/schemas&quot;;<br>import { activateUser } from &quot;@/domain/users&quot;;<br><br>export async function handleRequest(rawInput: unknown) {<br>  const payload = z.object({<br>    id: UserIdSchema,<br>    email: EmailSchema,<br>  }).safeParse(rawInput);<br><br>  if (!payload.success) {<br>    return { error: &quot;Invalid data&quot; };<br>  }<br>  // Payload.data.id is now typed as `UserId` automatically!<br>  await activateUser(payload.data.id, payload.data.email);<br>}</pre><h3>3. The Database Layer (Drizzle ORM)</h3><p>If your database returns plain strings, you lose the safety chain. Drizzle ORM allows us to enforce brands at the schema level using $type.</p><p>TypeScript</p><pre>// db/schema.ts<br>import { pgTable, text } from &quot;drizzle-orm/pg-core&quot;;<br>import { UserId } from &quot;@/types&quot;;<br><br><br>export const users = pgTable(&quot;users&quot;, {<br>  // Tell Drizzle: &quot;This column is a UserId, not just a string&quot;<br>  id: text(&quot;id&quot;).$type&lt;UserId&gt;().primaryKey(), <br>  name: text(&quot;name&quot;),<br>});</pre><p>Now, when you run db.select().from(users), the result automatically has id: UserId. The safety extends from your database all the way to your frontend components.</p><h3>When Should You Use This?</h3><p>I do not brand everything. Branding FirstName or Description is usually overkill because those are just text.</p><p>I use the <strong>“Identity &amp; Criticality”</strong> rule. I apply brands to three specific categories.</p><ul><li><strong>IDs:</strong> UserId, OrderId, ProductId. Mixing these up is fatal to data integrity.</li><li><strong>Units of Measure:</strong> Seconds, Milliseconds, Cents. NASA lost a Mars orbiter because of this error, and you might just lose a customer.</li><li><strong>Sensitive Data:</strong> HashedPassword, SanitizedHtml. This prevents you from accidentally rendering a password to the UI.</li></ul><h3>Final Thoughts</h3><p>Branded Types represent more than just a clever TypeScript hack. They are a declaration of intent. They allow us to write code that aligns with <strong>Domain-Driven Design</strong>, where our function signatures speak the language of the business rather than the language of the CPU.</p><p>By combining the compile-time guarantees of Branded Types with the runtime verification of Zod, you create a defense-in-depth strategy that eliminates entire categories of bugs before they ever reach production.</p><p>Stop passing strings. Start passing meaning.</p><p>Original post on my personal blog. <a href="https://www.gperrucci.com/en/blog/typescript/branded-types-zod-senior-engineer-secret-safety">Click here to read the full article</a></p><h3>References &amp; Resources</h3><p>If you want to dive deeper into the tools and concepts mentioned in this article, here are the official resources:</p><ul><li><strong>Zod Documentation:</strong> <a href="https://zod.dev/">zod.dev</a></li><li><strong>Learning TypeScript (Branded Types):</strong> <a href="https://www.learningtypescript.com/articles/branded-types">learningtypescript.com/articles/branded-types</a></li><li><strong>Drizzle ORM Custom Types:</strong> <a href="https://orm.drizzle.team/docs/custom-types">orm.drizzle.team/docs/custom-types</a></li><li><strong>Deep Dive on Branded Types (Video):</strong>(<a href="https://www.youtube.com/watch?v=aP6w2OzidYM">https://www.youtube.com/watch?v=aP6w2OzidYM</a>)</li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=2e4a55438ae3" width="1" height="1" alt=""><hr><p><a href="https://medium.com/javascript-render/branded-types-zod-the-senior-engineers-secret-to-safety-2e4a55438ae3">Branded Types &amp; Zod: The Senior Engineer’s Secret to Safety</a> was originally published in <a href="https://medium.com/javascript-render">Javascript &amp; Render</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Styled Components Are Killing Your App’s Performance]]></title>
            <link>https://medium.com/javascript-render/styled-components-are-killing-your-apps-performance-b6237f0f47f3?source=rss----da9975776266---4</link>
            <guid isPermaLink="false">https://medium.com/p/b6237f0f47f3</guid>
            <category><![CDATA[nextjs]]></category>
            <category><![CDATA[css-in-js]]></category>
            <category><![CDATA[zero-runtime]]></category>
            <category><![CDATA[styled-components]]></category>
            <category><![CDATA[react]]></category>
            <dc:creator><![CDATA[Gerardo Perrucci]]></dc:creator>
            <pubDate>Thu, 11 Dec 2025 20:11:31 GMT</pubDate>
            <atom:updated>2025-12-11T20:11:28.793Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*qKoEeLANxYp7TEBAnV7aAw.png" /><figcaption>Styled-Components Migration</figcaption></figure><p>I remember exactly when I fell out of love with <strong>Styled Components</strong>.</p><p>For years, it was my go-to. <strong>The developer experience felt like magic</strong>. I could pass props to my styles! I could use logic inside my CSS! It felt like the future.</p><p>Then, I led the development of a massive design system for a fintech client. We weren’t building a simple landing page; we were building complex dashboards with thousands of DOM nodes.</p><p><em>This is just the tip of the iceberg. I break down the full strategy with examples in the original post on my personal blog. </em><a href="https://www.gperrucci.com/en/blog/react/why-migrating-design-systems-away-from-styled-components"><em>Click here to read the full article</em></a></p><p>Suddenly, that “<em>magic</em>” started to feel heavy. The application became sluggish on lower-end devices. The initial load times crept up.</p><p>We realized the tool that helped us move fast in the beginning was now the very thing slowing us down.</p><h3>1. The “Runtime Tax” is Too Expensive</h3><p>Here is the simple truth:</p><blockquote><strong>Styled Components forces the browser to do extra work.</strong></blockquote><p>When your user loads a page, the browser has to download the JavaScript, parse it, execute it, and <em>then</em> inject the styles into the document.</p><blockquote>It’s a lot of steps just to make a button blue.</blockquote><p>On a small blog, you won’t notice it. But on an enterprise dashboard with data tables, visualizations, and hundreds of interactive elements? That overhead stacks up fast.</p><p>We found that by moving to a zero-runtime solution (like CSS Modules or Tailwind), we weren’t just changing syntax; we were giving the CPU a break.</p><h3>2. It Fights Against Server Components</h3><p>The React ecosystem is changing. With <strong>Next.js </strong>and the rise of Server Components, the goal is to do as much work as possible on the server before it ever reaches the user.</p><p>Runtime CSS-in-JS libraries like Styled Components are inherently client-side heavy. They need the browser to work their logic.</p><p><strong>Trying to force them to play nice with the new App Router in Next.js felt like swimming upstream. We ended up with “use client” directives everywhere, effectively opting out of the performance benefits we were trying to gain.</strong></p><h3>3. Debugging Became a Nightmare</h3><p>Have you ever tried to debug a production issue and looked at the DOM, only to see this?</p><p>&lt;div class=&quot;sc-bdVaJa bDWFJH&quot;&gt;...&lt;/div&gt;</p><p>Those random hashes are useless.</p><p>When you are managing a team and trying to fix a critical UI bug, clarity is everything. We missed semantic class names. We missed being able to look at the HTML and instantly know what component we were dealing with.</p><p><strong>Migrating away gave us our readability back.</strong></p><h3>The Conclusion</h3><p>I still think Styled Components is a fine tool for smaller projects or rapid prototyping. But engineering is about trade-offs.</p><p>When you are building for scale, performance isn’t just a “nice to have”, it’s a feature. We realized that shipping JavaScript just to generate CSS was a trade-off we could no longer afford.</p><p><em>This is just the tip of the iceberg. I break down the full strategy with examples in the original post on my personal blog. </em><a href="https://www.gperrucci.com/en/blog/react/why-migrating-design-systems-away-from-styled-components"><em>Click here to read the full article</em></a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=b6237f0f47f3" width="1" height="1" alt=""><hr><p><a href="https://medium.com/javascript-render/styled-components-are-killing-your-apps-performance-b6237f0f47f3">Styled Components Are Killing Your App’s Performance</a> was originally published in <a href="https://medium.com/javascript-render">Javascript &amp; Render</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[How I Structure Next.js Projects for Scale]]></title>
            <link>https://medium.com/javascript-render/how-i-structure-next-js-projects-for-scale-80646889fd4e?source=rss----da9975776266---4</link>
            <guid isPermaLink="false">https://medium.com/p/80646889fd4e</guid>
            <category><![CDATA[react]]></category>
            <category><![CDATA[folder-structure]]></category>
            <category><![CDATA[nextjs]]></category>
            <dc:creator><![CDATA[Gerardo Perrucci]]></dc:creator>
            <pubDate>Sun, 07 Dec 2025 17:28:42 GMT</pubDate>
            <atom:updated>2025-12-07T17:28:42.181Z</atom:updated>
            <content:encoded><![CDATA[<p>When a Next.js project is new, almost any structure feels fine. Six months later — after product pivots, new requirements, and a couple of rushed deadlines — the same repo can easily turn into a maze of duplicated components, copy-pasted hooks, and “<strong><em>mystery</em></strong>” utilities.</p><p>From working on high-traffic gaming portals and complex fintech dashboards, one thing has been very clear to me: <strong>the way you structure a Next.js app is as important as the stack itself</strong>. Next.js, TypeScript, React, TanStack Query… they all help — but without a clear architecture, the project will slow you down.</p><p>This is the condensed version of how I structure Next.js projects to stay fast, discoverable, and scalable over time. For the full deep dive, including more code and context, you can read the original article on my blog: <em>How I Structure Next.js Projects for Scale</em> (<a href="https://www.gperrucci.com/en/blog/react/how-i-structure-nextjs-projects-for-scale">gperrucci.com</a>)</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*cxVxK0ZTdREACoZSmHOylg.png" /></figure><h3>From “components/hooks/utils” to Feature-Based Architecture</h3><p>Most tutorials start with a <strong>technical</strong> folder split:</p><ul><li>components/</li><li>hooks/</li><li>utils/</li></ul><p>This works for a portfolio or a simple marketing site. But in real products — multi-tenant platforms, financial dashboards, back-office tools — this style actively hurts you:</p><ul><li>You need to jump across 3–5 folders just to change one feature.</li><li>Business concepts are scattered: API calls in lib/ or services/, components in components/, types in types/…</li></ul><p>Instead, I organize by <strong>feature / business domain</strong>.</p><p>If I delete src/features/invoices, I expect to remove <em>everything</em> related to invoices: components, hooks, API layer, and types. That’s my test for a good feature boundary.</p><p>So the core principle is:</p><blockquote><strong><em>Group by feature, not by file type.</em></strong></blockquote><h3>The High-Level Folder Layout</h3><p>Here’s the layout I use as a default for production-grade Next.js (App Router) apps:</p><pre>src/<br>├── app/                  # Routing &amp; layouts only<br>│   ├── (public)/<br>│   ├── (dashboard)/<br>│   ├── layout.tsx<br>│   └── page.tsx<br>├── components/           # Shared UI (&quot;Design System&quot;)<br>│   ├── button/<br>│   ├── modal/<br>│   └── typography/<br>├── features/             # Business domains<br>│   ├── auth/<br>│   ├── settings/<br>│   └── financial-risk/<br>├── lib/                  # 3rd-party wiring &amp; config<br>│   ├── axios.ts<br>│   ├── query-client.ts<br>│   └── utils.ts<br>└── types/                # Global types (keep small)</pre><p>Each of these top-level folders has a very specific responsibility.</p><h3>Keeping app/ Thin: Routes, Not Business Logic</h3><p>The <strong>App Router</strong> is fantastic for layouts and data-fetching, but that doesn’t mean your page.tsx files should become 300-line components.</p><p>I treat each page.tsx as an <strong>entry point</strong> that hands off almost immediately to a feature.</p><p><strong>Bad pattern</strong>: doing everything in the route file.</p><pre>// ❌ app/dashboard/page.tsx<br>&#39;use client&#39;;<br>export default function DashboardPage() {<br>  // Local state, effects, complex logic, transformations...<br>  // 50+ lines of behavior mixed with JSX<br>  return &lt;div&gt;...&lt;/div&gt;;<br>}</pre><p><strong>Preferred pattern</strong>: delegate to a feature.</p><pre>// ✅ app/dashboard/page.tsx<br>import { DashboardOverview } from &#39;@/features/dashboard/components/dashboard-overview&#39;;<br>export default function DashboardPage() {<br>  return &lt;DashboardOverview /&gt;;<br>}</pre><p>This gives you:</p><ul><li>Simpler routing layer.</li><li>Cleaner separation between URL structure and business logic.</li><li>Easier refactors when product requirements evolve.</li></ul><h3>The features/ Directory: Where the Real Work Lives</h3><p>The features/ folder is where 80–90% of your time should be spent. Each feature behaves like a mini-application, with clear internal structure:</p><pre>src/features/financial-risk/<br>├── components/           # Only for this feature<br>│   ├── risk-chart.tsx<br>│   └── risk-table.tsx<br>├── hooks/                # Feature-specific hooks<br>│   └── use-risk-calculations.ts<br>├── api/                  # Data access for this domain<br>│   └── get-risk-data.ts<br>└── types/                # Domain types<br>    └── index.ts</pre><p>Some guidelines:</p><ul><li><strong>Don’t</strong> put shared-looking code here too early. It’s fine if risk-table.tsx initially duplicates some logic from positions-table.tsx.</li><li><strong>Do</strong> keep everything that belongs to the domain together: UI, hooks, data access, and types.</li></ul><p>When a feature becomes obsolete, you remove one folder and you’re done. No orphaned files hiding in utils/.</p><h3>The components/ Directory: Your Internal Design System</h3><p>src/components/ is reserved for <strong>shared UI</strong>, not business logic.</p><p>Think of it as your local design system:</p><ul><li>Buttons, modals, form controls, layout primitives.</li><li>No knowledge of API calls, business rules, or domain terms like “invoice” or “risk”.</li></ul><p>A button component should care about things like variant and size, not about “approve invoice” vs “reject invoice”. That logic lives in your features.</p><p>The benefit is two-fold:</p><ul><li>Product teams can move fast inside features without worrying about breaking other areas.</li><li>Design consistency is enforced through a small set of shared components.</li></ul><h3>Example: A Transaction Feature Built for Scale</h3><p>Let’s connect the pieces with a concrete example: a <strong>Transactions</strong> feature using TypeScript and TanStack Query.</p><p><strong>1. API layer</strong> — encapsulate server access for this domain:</p><pre>// src/features/transactions/api/get-transactions.ts<br>import { axios } from &#39;@/lib/axios&#39;;<br>import type { Transaction } from &#39;../types&#39;;<br>export const getTransactions = async (): Promise&lt;Transaction[]&gt; =&gt; {<br>  const response = await axios.get(&#39;/transactions&#39;);<br>  return response.data;<br>};</pre><p><strong>2. Typed model</strong> — strictly scoped to the feature:</p><pre>// src/features/transactions/types/index.ts<br>export interface Transaction {<br>  id: string;<br>  amount: number;<br>  currency: string;<br>  createdAt: string;<br>}</pre><p><strong>3. UI component</strong> — co-locates the behavior and rendering:</p><pre>// src/features/transactions/components/transaction-list.tsx<br>&#39;use client&#39;;<br>import { useQuery } from &#39;@tanstack/react-query&#39;;<br>import { getTransactions } from &#39;../api/get-transactions&#39;;<br>import type { Transaction } from &#39;../types&#39;;<br>import { Spinner } from &#39;@/components/ui/spinner&#39;;<br>const TransactionItem = ({ transaction }: { transaction: Transaction }) =&gt; (<br>  &lt;li&gt;<br>    {transaction.amount} {transaction.currency} –{&#39; &#39;}<br>    {new Date(transaction.createdAt).toLocaleString()}<br>  &lt;/li&gt;<br>);<br>export const TransactionList = () =&gt; {<br>  const { data, isLoading, isError } = useQuery({<br>    queryKey: [&#39;transactions&#39;],<br>    queryFn: getTransactions,<br>  });<br>  if (isLoading) return &lt;Spinner /&gt;;<br>  if (isError) return &lt;div&gt;Failed to load transactions&lt;/div&gt;;<br>  return (<br>    &lt;ul className=&quot;space-y-2&quot;&gt;<br>      {data?.map((tx) =&gt; (<br>        &lt;TransactionItem key={tx.id} transaction={tx} /&gt;<br>      ))}<br>    &lt;/ul&gt;<br>  );<br>};</pre><p><strong>4. Route file</strong> — thin entry point:</p><pre>// app/(dashboard)/transactions/page.tsx<br>import { TransactionList } from &#39;@/features/transactions/components/transaction-list&#39;;<br>export default function TransactionsPage() {<br>  return &lt;TransactionList /&gt;;<br>}</pre><p>With this setup:</p><ul><li>Routing is clean and focused.</li><li>Transactions logic lives entirely in features/transactions.</li><li>Shared UI (Spinner) comes from components/.</li><li>Server state is handled by TanStack Query, not by ad-hoc state sprinkled across the app.</li></ul><h3>State Management: Lean Hard on Server State</h3><p>A frequent mistake in React apps is to push everything into global state (Redux, Zustand, contexts) even when the data actually lives on the server.</p><p>My rule of thumb:</p><ol><li><strong>Comes from an API or DB?</strong> Use <strong>TanStack Query</strong> (server state).</li><li><strong>Represents filters, sorting, pagination?</strong> Use <strong>URL search params</strong> (Next.js useSearchParams).</li><li><strong>Pure UI concerns (dialog open, selected tab)?</strong> Use <strong>local </strong><strong>useState</strong> in the closest component.</li><li><strong>Truly global UI (theme, layout toggles) or cross-feature coordination?</strong> Then consider context or a small Zustand store.</li></ol><p>This keeps global state small and targeted, while the majority of your data flows through well-understood server-state tooling.</p><h3>When to Abstract: The “Rule of Two”</h3><p>Engineers often abstract too early: a “shared” component appears the first time a pattern is used, and then it slowly turns into a massive God component that everybody is afraid to touch.</p><p>I try to follow a simple rule:</p><blockquote><strong><em>Don’t move a component into </em></strong><strong><em>src/components/ until it’s used in at least two different features.</em></strong></blockquote><p>Before that, leave it inside the feature, even if it looks generic. Duplicating a small chunk of code is cheaper than maintaining the wrong abstraction across the entire codebase.</p><p>Once you genuinely see the same behavior in multiple features, you can extract a cleaner, more accurate shared component.</p><p>For the full version of this article — with more detail, screenshots, and additional nuances around migration and team workflows — you can read it on my blog: <a href="https://www.gperrucci.com/en/blog/react/how-i-structure-nextjs-projects-for-scale"><em>How I Structure Next.js Projects for Scale</em></a>.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=80646889fd4e" width="1" height="1" alt=""><hr><p><a href="https://medium.com/javascript-render/how-i-structure-next-js-projects-for-scale-80646889fd4e">How I Structure Next.js Projects for Scale</a> was originally published in <a href="https://medium.com/javascript-render">Javascript &amp; Render</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[The Mental Shift: Unlearning Next.js 12 to Master the App Router]]></title>
            <link>https://medium.com/javascript-render/the-mental-shift-unlearning-next-js-12-to-master-the-app-router-5c1e0def88e2?source=rss----da9975776266---4</link>
            <guid isPermaLink="false">https://medium.com/p/5c1e0def88e2</guid>
            <category><![CDATA[nextjs]]></category>
            <category><![CDATA[nextjs-app-router]]></category>
            <category><![CDATA[migration]]></category>
            <dc:creator><![CDATA[Gerardo Perrucci]]></dc:creator>
            <pubDate>Sat, 06 Dec 2025 21:25:04 GMT</pubDate>
            <atom:updated>2025-12-06T21:25:04.536Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*o2QO9ixA9OG21ofSXItFkw.png" /></figure><p><em>This is a condensed version of my original article published on my blog:<br> </em><strong><em>“The Mental Shift: Unlearning Next.js 12 to Master the App Router”</em></strong><em><br> 👉 </em><a href="https://www.gperrucci.com/en/blog/react/mental-shift-nextjs-12-app-router"><em>https://www.gperrucci.com/en/blog/react/mental-shift-nextjs-12-app-router</em></a></p><p>If you have spent years working with Next.js 12, you probably move through a familiar ritual:</p><ul><li>Add getServerSideProps or getStaticProps without thinking.</li><li>Wire global state in _app.tsx.</li><li>Fight waterfalls and layout shifts when data comes from the client.</li></ul><p>Upgrading to Next.js 14/15 with the <strong>App Router</strong> is not “Next.js 12 with a nicer API.” It feels much closer to switching frameworks.</p><p>The real work is not learning new functions. It is <strong>unlearning</strong> the old mental model and replacing it with a <strong>server-first way of thinking</strong> powered by React Server Components (RSC).</p><p>This article is about that mental reset.</p><h3>The Old World: The “Thick Client” Illusion</h3><p>In the Pages Router era, the pipeline looked like this:</p><ol><li>The server runs getServerSideProps / getStaticProps, fetches data, and renders HTML.</li><li>The browser receives HTML + JSON + a JavaScript bundle.</li><li>React hydrates on the client and takes over.</li></ol><p>Even when the server did some work, the real “application” still lived in the browser. Your React tree always shipped as JavaScript, and hydration was the price you paid to get interactivity.</p><p>This had some painful side effects:</p><ul><li>Deeply nested components needing data forced you into <strong>prop drilling</strong> from the page level.</li><li>Or you shifted fetching into the client (via useEffect, React Query, etc.) and accepted waterfalls and loading spinners.</li><li>Global state grew just to keep the UI in sync with APIs that really belonged closer to the backend.</li></ul><p>We spent a lot of time optimizing around constraints that were baked into this architecture.</p><h3>The New Reality: Server Components as the Default</h3><p>The App Router introduces <strong>React Server Components</strong> and quietly flips the entire model:</p><ul><li>In app/, components are <strong>Server Components by default</strong>.</li><li>They render on the server only.</li><li>They never hydrate, and their code is never shipped to the browser.</li></ul><p>Instead of your component being a template waiting for data, the component itself can <strong>be the backend boundary</strong>:</p><ul><li>It can read cookies and headers.</li><li>It can call your database directly.</li><li>It can use secrets without exposing them to the client.</li></ul><p>This is why the App Router feels so different. You’re no longer bolting server behavior onto a client app; you’re building a server-first tree with small, explicit islands of interactivity.</p><h3>A Concrete Example: Dashboard in Next 12 vs App Router</h3><p>Let’s look at a common scenario: a dashboard that needs a user profile and settings.</p><h3>The Next.js 12 Way (Pages Router)</h3><pre>// pages/dashboard.tsx<br>import { GetServerSideProps } from &#39;next&#39;<br>import { User, Settings } from &#39;../types&#39;<br>interface Props {<br>  user: User<br>  settings: Settings<br>}<br>export const getServerSideProps: GetServerSideProps&lt;Props&gt; = async (ctx) =&gt; {<br>  const userRes = await fetch(&#39;https://api.internal/user&#39;, {<br>    headers: { Cookie: ctx.req.headers.cookie || &#39;&#39; },<br>  })<br>  const user = await userRes.json()<br>  const settingsRes = await fetch(<br>    `https://api.internal/settings/${user.id}`,<br>  )<br>  const settings = await settingsRes.json()<br>  return { props: { user, settings } }<br>}<br><br>export default function Dashboard({ user, settings }: Props) {<br>  return (<br>    &lt;main&gt;<br>      &lt;h1&gt;Welcome, {user.name}&lt;/h1&gt;<br>      &lt;SettingsPanel data={settings} /&gt;<br>    &lt;/main&gt;<br>  )<br>}</pre><p>Here the data-fetching logic is detached from the component tree. If SettingsPanel needs more data later, you go back to getServerSideProps, change the query, and drill more props down.</p><p>The page is not self-contained; the <em>data pipeline</em> lives elsewhere.</p><h3>The App Router Way (Server Components)</h3><p>Now with the App Router, we colocate data and UI in <strong>async Server Components</strong>.</p><pre>// app/dashboard/page.tsx<br>import { cookies } from &#39;next/headers&#39;<br>import { SettingsPanel } from &#39;./_components/settings-panel&#39;<br>export default async function DashboardPage() {<br>  const cookieStore = await cookies()<br>  const token = cookieStore.get(&#39;auth_token&#39;)?.value<br>  const user = await getUser(token)<br>  return (<br>    &lt;main&gt;<br>      &lt;h1&gt;Welcome, {user.name}&lt;/h1&gt;<br>      {/* SettingsPanel can fetch its own data */}<br>      &lt;SettingsPanel userId={user.id} /&gt;<br>    &lt;/main&gt;<br>  )<br>}</pre><pre>// app/dashboard/_components/settings-panel.tsx<br>async function SettingsPanel({ userId }: { userId: string }) {<br>  const settings = await db.settings.findUnique({ where: { userId } })<br>  return (<br>    &lt;section&gt;<br>      &lt;h2&gt;Settings&lt;/h2&gt;<br>      &lt;pre&gt;{JSON.stringify(settings, null, 2)}&lt;/pre&gt;<br>    &lt;/section&gt;<br>  )<br>}</pre><p>The important part is <strong>what changed</strong>:</p><ul><li>Both DashboardPage and SettingsPanel run on the server.</li><li>They fetch their own data.</li><li>The browser receives fully rendered HTML for the user and settings, with <strong>zero client-side fetches</strong> needed for this view.</li></ul><p>In the Next 12 mindset, this pattern would usually push SettingsPanel into client-side fetching. In the App Router mindset, you keep it server-side, closer to your data.</p><p>That’s the “<strong>aha</strong>” moment for most teams.</p><h3>The Heuristic: When to Write &quot;use client&quot;</h3><p>One of the first questions people ask is:</p><blockquote><em>“So do we just never use client components anymore?”</em></blockquote><p>You absolutely still use them, but for a different reason.</p><p>Instead of using them for data fetching, you use them for <strong>interactivity</strong>.</p><p>This is the rule I work with:</p><ul><li>If a component mostly <strong>reads data</strong> (DB queries, API calls, static content) → keep it as a <strong>Server Component</strong>.</li><li>If a component needs to <strong>listen to the user</strong> (onClick, onChange, useState, useEffect) → make it a <strong>Client Component</strong>.</li></ul><p>You opt into client behavior explicitly with &quot;use client&quot; at the top of the file:</p><pre>// app/components/like-button.tsx<br>&#39;use client&#39;<br>import { useState } from &#39;react&#39;<br>export default function LikeButton() {<br>  const [likes, setLikes] = useState(0)<br>  return (<br>    &lt;button onClick={() =&gt; setLikes((prev) =&gt; prev + 1)}&gt;<br>      Like {likes}<br>    &lt;/button&gt;<br>  )<br>}</pre><p>You then embed &lt;LikeButton /&gt; inside a Server Component:</p><pre>// app/post/[slug]/page.tsx (server)<br>import LikeButton from &#39;@/app/components/like-button&#39;<br>export default async function PostPage() {<br>  const post = await getPostFromDB()<br>  return (<br>    &lt;article&gt;<br>      &lt;h1&gt;{post.title}&lt;/h1&gt;<br>      &lt;p&gt;{post.body}&lt;/p&gt;<br>      &lt;LikeButton /&gt;<br>    &lt;/article&gt;<br>  )<br>}</pre><p>The result:</p><ul><li>The post content is server-rendered, cacheable, and SEO-friendly.</li><li>Only the small LikeButton island hydrates and runs in the browser.</li></ul><p>You start thinking in terms of <strong>interactive islands</strong> inside a server-rendered tree, not one big client app with some SSR sprinkled in.</p><h3>Pros, Cons, and Reality Checks</h3><p>This new model comes with real benefits, but also real friction.</p><h3>Why It’s Worth It</h3><p>You gain:</p><ul><li><strong>Smaller bundles</strong> — server component code never ships to the browser. Heavy utilities and parsing libraries stay on the server.</li><li><strong>Better security</strong> — database queries and secrets live directly in server components, no extra API layer required just for rendering.</li><li><strong>Clearer boundaries</strong> — components can own their data and markup instead of acting as passive templates.</li></ul><p>In practice, this usually translates to better performance and a simpler data flow, especially in large dashboards and content-heavy apps.</p><h3>The Friction You Will Feel</h3><p>You also have to accept new constraints:</p><ul><li>You must track <strong>where</strong> a component runs. Trying to use useState in a Server Component will fail, and that’s by design.</li><li>Some library ecosystems are still catching up to RSC, especially if they assume a browser environment everywhere.</li><li>Caching behavior in the App Router is more opinionated and more aggressive; the old mental model of “getServerSideProps always runs on every request” no longer applies.</li></ul><p>There is a learning curve, and you will hit it.</p><h3>Original Article &amp; Further Reading</h3><p>You can find the full, extended version of this article here:<br> 👉 <a href="https://www.gperrucci.com/en/blog/react/mental-shift-nextjs-12-app-router">https://www.gperrucci.com/en/blog/react/mental-shift-nextjs-12-app-router</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=5c1e0def88e2" width="1" height="1" alt=""><hr><p><a href="https://medium.com/javascript-render/the-mental-shift-unlearning-next-js-12-to-master-the-app-router-5c1e0def88e2">The Mental Shift: Unlearning Next.js 12 to Master the App Router</a> was originally published in <a href="https://medium.com/javascript-render">Javascript &amp; Render</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[CSS Subgrid: Making Complex Layouts Behave]]></title>
            <link>https://medium.com/javascript-render/css-subgrid-making-complex-layouts-behave-ab095308e6df?source=rss----da9975776266---4</link>
            <guid isPermaLink="false">https://medium.com/p/ab095308e6df</guid>
            <category><![CDATA[css3]]></category>
            <category><![CDATA[css-subgrid]]></category>
            <category><![CDATA[subgrid]]></category>
            <category><![CDATA[software-engineering]]></category>
            <category><![CDATA[grid]]></category>
            <dc:creator><![CDATA[Gerardo Perrucci]]></dc:creator>
            <pubDate>Mon, 24 Nov 2025 17:32:20 GMT</pubDate>
            <atom:updated>2025-11-24T17:32:19.671Z</atom:updated>
            <content:encoded><![CDATA[<blockquote>If you have ever built a grid of cards where the titles, text and “Read more” links stubbornly refuse to line up, you already know how painful nested layouts can be. You tweak min-height, duplicate track definitions, even reach for display: contents, and it still feels brittle.</blockquote><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*flAZXIuHQJwKH69G-dRqDg.png" /><figcaption>css subgrid</figcaption></figure><p>CSS <strong>subgrid</strong> is the feature that finally fixes this. It lets nested grids reuse the <em>exact same tracks</em> as their parent, so components align as if they were on a single shared grid — even when they live several levels deep in the DOM.</p><p>This article is a concise version of my full deep dive, which you can read here:<br> 👉 <strong>Original article:</strong> <a href="https://www.gperrucci.com/en/blog/css/css-subgrid-consistent-complex-layouts">https://www.gperrucci.com/en/blog/css/css-subgrid-consistent-complex-layouts</a></p><p>👉 <strong>Live code example (StackBlitz):</strong> <a href="https://stackblitz.com/~/github.com/centrodph/subgrid-example">https://stackblitz.com/~/github.com/centrodph/subgrid-example</a></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*sMBQE0LiQCAHL3fzQ3J54g.png" /><figcaption>Subgrid</figcaption></figure><h3>What Subgrid Really Does (In One Paragraph)</h3><p>With normal CSS Grid, every grid container defines its own rows and columns. A child grid has no idea what tracks exist on the parent; it is a separate layout world. Subgrid changes that: when you set grid-template-rows: subgrid or grid-template-columns: subgrid on a child grid, it <strong>inherits the track sizes and gaps</strong> from the parent area it spans. The child still controls how its own items are placed, but the tracks themselves are shared.</p><p>That means your cards, sections or nested layouts can all line up vertically and horizontally without copy-pasting grid-template-rows and hoping nothing drifts out of sync.</p><h3>Why Nested Grids Used To Be So Awkward</h3><p>Imagine a common layout: a responsive grid of cards with a title, body and footer.</p><p>Without subgrid, you usually do something like:</p><pre>.cards {<br>  display: grid;<br>  grid-template-columns: repeat(auto-fit, minmax(18rem, 1fr));<br>  gap: 1.5rem;<br>}</pre><pre>.card {<br>  display: grid;<br>  grid-template-rows: auto 1fr auto; /* duplicated everywhere */<br>}</pre><p>Each .card is an independent grid. The tallest card determines the column height, but internal rows are not shared, so titles and footers only “kind of” align. If you change the parent’s track logic, you must manually keep every nested grid in sync.</p><p>Some developers tried to cheat with display: contents so grandchildren become direct children of the main grid. That helps alignment, but it breaks the idea of a “card” as a real layout boundary and can confuse accessibility tools and devtools.</p><h3>Enter Subgrid: One Grid, Many Levels</h3><p>With subgrid, you keep the card as a proper grid item <em>and</em> a grid container, but you allow its row tracks to come from the parent grid.</p><h3>HTML</h3><pre>&lt;section class=&quot;section&quot;&gt;<br>  &lt;h1 class=&quot;heading&quot;&gt;Latest articles&lt;/h1&gt;<br>  &lt;div class=&quot;cards&quot;&gt;<br>    &lt;article class=&quot;card&quot;&gt;<br>      &lt;h2 class=&quot;card__title&quot;&gt;Subgrid for perfectly aligned cards&lt;/h2&gt;<br>      &lt;p class=&quot;card__excerpt&quot;&gt;<br>        Use CSS subgrid to keep titles, excerpts and CTAs aligned across cards.<br>      &lt;/p&gt;<br>      &lt;footer class=&quot;card__meta&quot;&gt;5 min read · Layout&lt;/footer&gt;<br>    &lt;/article&gt;<br>    &lt;!-- More .card elements... --&gt;<br>  &lt;/div&gt;<br>&lt;/section&gt;</pre><h3>CSS with subgrid</h3><pre>.section {<br>  padding: 2rem clamp(1.5rem, 4vw, 4rem);<br>  background: #020617;<br>  color: #e5e7eb;<br>}<br>.heading {<br>  font-size: clamp(1.5rem, 3vw, 2rem);<br>  margin-bottom: 1.5rem;<br>}<br>/* Parent grid defines the “layout contract” */<br>.cards {<br>  display: grid;<br>  grid-template-columns: repeat(auto-fit, minmax(18rem, 1fr));<br>  grid-template-rows: auto 1fr auto; /* title / body / footer */<br>  gap: 1.5rem;<br>}<br>/* Each card becomes a subgrid for rows */<br>.card {<br>  display: grid;<br>  grid-template-rows: subgrid;  /* reuse auto 1fr auto */<br>  grid-row: auto / span 3;      /* span those 3 parent tracks */<br>  padding: 1.25rem 1.5rem;<br>  border-radius: 0.75rem;<br>  background: #020617;<br>  border: 1px solid #1f2933;<br>}<br>/* Children align to the shared row tracks */<br>.card__title {<br>  grid-row: 1;<br>  margin: 0 0 0.5rem;<br>  font-size: 1.125rem;<br>  font-weight: 600;<br>}<br>.card__excerpt {<br>  grid-row: 2;<br>  margin: 0 0 0.75rem;<br>  font-size: 0.95rem;<br>  line-height: 1.5;<br>}<br>.card__meta {<br>  grid-row: 3;<br>  margin-top: auto;<br>  font-size: 0.85rem;<br>  opacity: 0.75;<br>}</pre><p>The key ideas:</p><ul><li>The <strong>parent</strong> grid defines the row tracks (auto 1fr auto) and the gap.</li><li>Each <strong>card</strong> declares grid-template-rows: subgrid and spans three rows, so it adopts those exact tracks.</li><li>The card’s children place themselves in row 1, 2 and 3, and every card now lines up <strong>perfectly</strong>, regardless of content length.</li></ul><p>If you want to see a full working example (including React/TypeScript structure), you can open the StackBlitz project here:<br> 👉 <a href="https://stackblitz.com/~/github.com/centrodph/subgrid-example">https://stackblitz.com/~/github.com/centrodph/subgrid-example</a></p><h3>Subgrid in Design Systems: Macro Grids and Shared Rhythm</h3><p>Subgrid becomes even more interesting when you think beyond a single card grid and start reasoning in terms of <strong>macro grids</strong>.</p><p>You can define a page-level or app-level grid with named rows and columns — [header], [main], [footer], [content-start], [content-end], and so on. Each major region (header, main content, sidebar, footer) becomes a grid that uses grid: subgrid / subgrid, so they all align to the same track system.</p><p>This connects directly to how many design systems are modelled in tools like Figma: a single grid that everything respects. Subgrid is the missing piece that allows your markup to mirror that concept without duplicating layout logic in every component.</p><h3>Browser Support and Fallback Strategy</h3><p>The good news: modern browsers finally support subgrid.</p><p>The practical approach is <strong>progressive enhancement</strong>:</p><pre>/* Baseline: normal nested grid */<br>.cards {<br>  display: grid;<br>  grid-template-columns: repeat(auto-fit, minmax(18rem, 1fr));<br>  gap: 1.5rem;<br>}</pre><pre>.card {<br>  display: grid;<br>  grid-template-rows: auto 1fr auto;<br>}</pre><pre>/* Enhancement for browsers that understand subgrid */<br>@supports (grid-template-rows: subgrid) {<br>  .cards {<br>    grid-template-rows: auto 1fr auto;<br>  }</pre><pre>  .card {<br>    grid-template-rows: subgrid;<br>    grid-row: auto / span 3;<br>  }<br>}</pre><p>Older browsers see a standard nested grid with “good enough” layout. Newer browsers get aligned rows and a cleaner layout model. You do not need JavaScript to “polyfill” subgrid; you simply give modern engines a better version of the same layout.</p><h3>When Subgrid Is Worth It</h3><p>Subgrid is not something you sprinkle randomly; it pays off most when:</p><ul><li>You have <strong>repeating components</strong> (cards, tiles, media items) that must align in both axes across a grid.</li><li>You maintain a <strong>design system</strong> and want a single source of truth for page layout.</li><li>You are already committed to CSS Grid and want to remove hacks like forced heights and display: contents wrappers.</li></ul><p>If your layout is simple, classic Grid or Flexbox may still be enough. But once you feel the pain of nested alignment, subgrid is usually the more honest and maintainable solution.</p><h3>Wrap-Up and Further Reading</h3><p>CSS subgrid closes a long-standing gap in CSS layout: it gives you nested components that behave like they live on the same grid, without throwing away encapsulation. It does not replace Grid; it <strong>extends</strong> it in a way that matches how we already think about pages and design systems.</p><p>If you want the complete deep dive with more examples, browser details and design-system patterns, you can read the original article here:</p><p>👉 <strong>Original article on my blog:</strong><br> <a href="https://www.gperrucci.com/en/blog/css/css-subgrid-consistent-complex-layouts">https://www.gperrucci.com/en/blog/css/css-subgrid-consistent-complex-layouts</a></p><p>And if you prefer to explore the code, tweak it and see the layout update instantly:</p><p>👉 <strong>Live StackBlitz demo:</strong><br> <a href="https://stackblitz.com/~/github.com/centrodph/subgrid-example">https://stackblitz.com/~/github.com/centrodph/subgrid-example</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=ab095308e6df" width="1" height="1" alt=""><hr><p><a href="https://medium.com/javascript-render/css-subgrid-making-complex-layouts-behave-ab095308e6df">CSS Subgrid: Making Complex Layouts Behave</a> was originally published in <a href="https://medium.com/javascript-render">Javascript &amp; Render</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Modern Front-End Development in Practice: From HTML & CSS to Angular, React, and Vue]]></title>
            <link>https://medium.com/javascript-render/modern-front-end-development-in-practice-from-html-css-to-angular-react-and-vue-bb3eb029bce8?source=rss----da9975776266---4</link>
            <guid isPermaLink="false">https://medium.com/p/bb3eb029bce8</guid>
            <category><![CDATA[software-engineering]]></category>
            <category><![CDATA[angular]]></category>
            <category><![CDATA[vuejs]]></category>
            <category><![CDATA[front-end-development]]></category>
            <category><![CDATA[react]]></category>
            <dc:creator><![CDATA[Gerardo Perrucci]]></dc:creator>
            <pubDate>Tue, 18 Nov 2025 07:08:34 GMT</pubDate>
            <atom:updated>2025-11-18T07:08:33.758Z</atom:updated>
            <content:encoded><![CDATA[<blockquote><em>“Any application that can be written in JavaScript, will eventually be written in JavaScript</em>,” Jeff Atwood wrote years ago.</blockquote><p>JavaScript has become the “duct tape of the Internet,” as Charlie Campbell put it, and modern front-end development is the proof.</p><p>When you browse an online store, filter products, compare prices, and complete a checkout, you are moving through a carefully orchestrated front-end stack: <strong>HTML</strong> for structure, <strong>CSS</strong> for design, <strong>JavaScript or TypeScript</strong> for behavior, and increasingly <strong>preprocessors and frameworks</strong> that keep everything maintainable as projects grow.</p><p>This article is a condensed version of the full guide. It focuses on what actually matters when you are deciding how to build front-end applications today.</p><p><a href="https://www.gperrucci.com/en/blog/engineering/modern-front-end-development-from-html-css-to-angular-react-and-vue"><em>Modern Front-End Development: From HTML and CSS to Angular, React, and Vue</em></a></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*f4hZ6zPkg978y8aA4WMZtA.png" /></figure><h3>1. The core: HTML, CSS, and JavaScript still matter</h3><p>Despite all the tooling noise, modern front-end work still rests on three pillars:</p><ul><li><strong>HTML</strong> defines <em>what</em> exists: headings, buttons, forms, images.</li><li><strong>CSS</strong> defines <em>how it looks</em>: layout, spacing, typography, responsive behavior.</li><li><strong>JavaScript / TypeScript</strong> define <em>what it does</em>: interactions, data fetching, dynamic updates.</li></ul><p>A tiny example captures the relationship:</p><pre>&lt;!-- index.html --&gt;<br>&lt;button id=&quot;login-button&quot;&gt;Log in&lt;/button&gt;<br>&lt;script type=&quot;module&quot; src=&quot;main.js&quot;&gt;&lt;/script&gt;</pre><pre>// main.ts – TypeScript compiled to JavaScript<br>const loginButton = document.querySelector&lt;HTMLButtonElement&gt;(&#39;#login-button&#39;);<br><br>if (!loginButton) {<br>  throw new Error(&#39;Login button not found&#39;);<br>}<br><br>loginButton.addEventListener(&#39;click&#39;, () =&gt; {<br>  // In a real app, you would call your backend here<br>  alert(&#39;Logging in...&#39;);<br>});</pre><p>HTML gives us a button, CSS (not shown) determines whether it looks trustworthy or sketchy, and TypeScript wires the behavior. Everything else in modern front-end stacks builds on top of this foundation.</p><p>If your fundamentals in HTML, CSS, and JavaScript are weak, frameworks will only hide the problem, not solve it. MDN’s documentation for <a href="https://developer.mozilla.org/en-US/docs/Web/HTML">HTML</a> and the <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Introduction">JavaScript guide</a> are still the best starting point.</p><h3>2. When plain CSS starts to hurt: Sass and Less</h3><p>Plain CSS works fine for a small marketing site. It becomes painful when you are styling a complex dashboard or e-commerce app:</p><ul><li>You repeat the same colors and spacing everywhere.</li><li>Simple refactors become dangerous because selectors are tangled.</li><li>You struggle to express “design tokens” like spacing scales or color palettes.</li></ul><p>That is where <strong>Sass</strong> and <strong>Less</strong> come in.</p><p><strong>Sass</strong> adds variables, nesting, mixins, and functions, then compiles down to plain CSS. You might define a $primary-color once and reuse it across components. The official docs at <a href="https://sass-lang.com/documentation">sass-lang.com</a> show how far you can push this, especially for design systems.</p><p><strong>Less</strong> offers similar capabilities with a slightly different syntax and the option to compile in the browser via less.js. It is still common in older design systems and legacy projects; the docs live at <a href="https://lesscss.org/">lesscss.org</a>.</p><p>In practice:</p><ul><li>For new projects with a build step, <strong>Sass</strong> is often the pragmatic choice.</li><li>For existing codebases already using <strong>Less</strong>, migrating just to follow trends rarely pays off.</li><li>For small sites, <strong>good plain CSS with custom properties</strong> is enough and keeps the toolchain simpler.</li></ul><p>Preprocessors are not mandatory, but they are often the difference between a stylesheet you can refactor confidently and one you are afraid to touch.</p><h3>3. Adaptive vs responsive: not just buzzwords</h3><p>The web course material you started from correctly highlights <strong>adaptive</strong> and <strong>responsive</strong> design, but many teams still conflate them.</p><ul><li><strong>Adaptive design</strong> creates distinct layouts for specific breakpoints (mobile, tablet, desktop). The system chooses which layout to serve. You may even change content per device class.</li><li><strong>Responsive design</strong> uses a single layout that fluidly reflows, relying on flexible grids, relative units, and functions like min(), max(), and clamp().</li></ul><p>Most modern products should default to responsive design: one system that adapts gracefully. Adaptive design is a conscious choice when you <em>want</em> different experiences, for example a radically simplified mobile checkout.</p><p>Whichever approach you choose, frameworks will not fix poor layout decisions. You need solid CSS skills either way.</p><h3>4. Angular, React, and Vue: what really distinguishes them</h3><p>As applications grow, direct DOM manipulation becomes unmanageable. You need components, routing, state management, and patterns that scale beyond a handful of pages. That is where frameworks come in — most teams standardize around <strong>Angular</strong>, <strong>React</strong>, or <strong>Vue</strong>.</p><h3>Angular — opinionated and integrated</h3><p><strong>Angular</strong> is a <strong>TypeScript-first framework</strong> with a strong architecture baked in. You get components, routing, dependency injection, forms, and HTTP utilities out of the box.</p><p>Angular works well when you have:</p><ul><li>Larger teams that benefit from <strong>strict conventions</strong>.</li><li>Enterprise-style apps that need clear layering and long-term maintainability.</li></ul><p>The trade-off: the learning curve is noticeably steeper, and the boilerplate can feel heavy for smaller or design-driven front-ends.</p><p>Official docs: <a href="https://angular.dev/">angular.dev</a> · GitHub: <a href="https://github.com/angular/angular">angular/angular</a></p><h3>React — flexible and ecosystem-driven</h3><p><strong>React</strong> is “the library for web and native user interfaces.” It focuses on the <strong>view layer</strong> and leaves routing, data fetching, and many architectural decisions to you and the ecosystem.</p><p>A minimal React + TypeScript component looks like this:</p><pre>// ProductCard.tsx<br>type ProductCardProps = {<br>  name: string;<br>  price: number;<br>  onAddToCart: () =&gt; void;<br>};<br><br>export const ProductCard = ({ name, price, onAddToCart }: ProductCardProps) =&gt; (<br>  &lt;article className=&quot;product-card&quot;&gt;<br>    &lt;h3&gt;{name}&lt;/h3&gt;<br>    &lt;p&gt;${price.toFixed(2)}&lt;/p&gt;<br>    &lt;button type=&quot;button&quot; onClick={onAddToCart}&gt;<br>      Add to cart<br>    &lt;/button&gt;<br>  &lt;/article&gt;<br>);</pre><p>React shines when:</p><ul><li>You need <strong>flexibility</strong> and want to compose your own stack (for example, Next.js, TanStack Query, Zustand).</li><li>You care about <strong>ecosystem depth</strong> and reusing logic across web and native (React Native).</li></ul><p>The downside is the same flexibility: you must make more decisions and keep up with a fast-moving ecosystem.</p><p>Official docs: <a href="https://react.dev/">react.dev</a> · GitHub: <a href="https://github.com/facebook/react">facebook/react</a></p><h3>Vue — approachable and progressive</h3><p><strong>Vue</strong> brands itself as a <strong>progressive framework</strong>: you can drop it into an existing page or build a full SPA with routing and state management.</p><p>It is attractive when:</p><ul><li>You want <strong>template syntax that feels very close to HTML</strong>.</li><li>You need to enhance server-rendered pages with pockets of interactivity instead of rewriting everything.</li></ul><p>Vue often feels like a middle ground between Angular’s structure and React’s flexibility, with a strong focus on developer experience.</p><p>Official docs: <a href="https://vuejs.org/guide/introduction.html">vuejs.org/guide</a> · GitHub: <a href="https://github.com/vuejs/core">vuejs/core</a></p><h3>So which one should you choose?</h3><p>There is no universal winner. A practical heuristic:</p><ul><li>Choose <strong>Angular</strong> when you want strong conventions, a complete toolbox, and are building long-lived, large-team applications.</li><li>Choose <strong>React</strong> when you need maximum flexibility, want to leverage a huge ecosystem, or plan to share code across platforms.</li><li>Choose <strong>Vue</strong> when you value an approachable learning curve and progressive adoption in existing projects.</li></ul><p>What matters more than the logo on your slide deck is <strong>consistency</strong>: once you choose a framework, define shared patterns for file structure, state management, data fetching, and testing so your UI does not collapse under incremental complexity.</p><h3>5. A realistic learning path (without chasing every trend)</h3><p>Modern front-end development is noisy. There is always a new build tool, a new meta-framework, a new state library. You do not need all of them.</p><p>For most developers, a pragmatic path looks like this:</p><ol><li><strong>Master HTML and CSS.</strong> Semantic markup, Flexbox, Grid, responsive design. No shortcuts.</li><li><strong>Get comfortable with JavaScript and TypeScript in the browser.</strong> Manipulate the DOM, handle events, call APIs with fetch.</li><li><strong>Add Sass or Less when your CSS starts to hurt.</strong> Capture design tokens in variables instead of copy-pasting values.</li><li><strong>Pick one framework — Angular, React, or Vue — and go deep.</strong> Build at least one real app with routing, forms, authentication, and error handling.</li><li><strong>Iterate on real products.</strong> Dashboards, admin panels, and e-commerce flows will teach you more than another to-do app ever will.</li></ol><p>If you focus on these fundamentals, you will be able to evaluate new tools on their merits instead of chasing them out of fear of missing out.</p><h3>Read the full article</h3><p>This is a condensed version of the full piece. If you want more detailed examples, including code for Angular, React, and Vue components, plus references to MDN and official docs, you can read the original here:</p><p>➡️ <strong>Full article:</strong><br> <a href="https://www.gperrucci.com/en/blog/engineering/modern-front-end-development-from-html-css-to-angular-react-and-vue">Modern Front-End Development: From HTML and CSS to Angular, React, and Vue</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=bb3eb029bce8" width="1" height="1" alt=""><hr><p><a href="https://medium.com/javascript-render/modern-front-end-development-in-practice-from-html-css-to-angular-react-and-vue-bb3eb029bce8">Modern Front-End Development in Practice: From HTML &amp; CSS to Angular, React, and Vue</a> was originally published in <a href="https://medium.com/javascript-render">Javascript &amp; Render</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Stop Testing Ghosts: Test Non-Existence with React Testing Library]]></title>
            <link>https://medium.com/javascript-render/stop-testing-ghosts-test-non-existence-with-react-testing-library-749ad1c82482?source=rss----da9975776266---4</link>
            <guid isPermaLink="false">https://medium.com/p/749ad1c82482</guid>
            <category><![CDATA[react-testing-library]]></category>
            <category><![CDATA[testing]]></category>
            <category><![CDATA[react]]></category>
            <dc:creator><![CDATA[Gerardo Perrucci]]></dc:creator>
            <pubDate>Mon, 10 Nov 2025 18:05:42 GMT</pubDate>
            <atom:updated>2025-11-10T18:05:41.759Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*r4U8DH0kNH3684A2b_ck7A.png" /></figure><p>When you test UI behavior, proving that “something is <strong>not there</strong>” is as important as proving that it <strong>is</strong>. The tricky part: elements can be present but invisible, scheduled to unmount, or living in a portal. Here’s the practical, reliable approach that avoids flakiness.</p><p><em>More reasoning and real-world tradeoffs in the full article:<br> </em><a href="https://www.gperrucci.com/en/blog/react/assert-non-existence-react-testing-library"><em>https://www.gperrucci.com/en/blog/react/assert-non-existence-react-testing-library</em></a></p><h3>The core rule (burn it into muscle memory)</h3><p><strong>Use </strong><strong>queryBy* + </strong><strong>not.toBeInTheDocument()</strong> for absence.</p><pre>import { screen } from &quot;@testing-library/react&quot;;<br>import &quot;@testing-library/jest-dom/extend-expect&quot;;<br>// Synchronous absence<br>expect(screen.queryByRole(&quot;dialog&quot;, { name: /profile/i })).not.toBeInTheDocument();<br><br>// TestID fallback when no accessible role/name fits<br>expect(screen.queryByTestId(&quot;role-icon&quot;)).not.toBeInTheDocument();</pre><p><strong>Why not </strong><strong>getBy*?</strong> Because getBy* throws if nothing is found, so your test never reaches the matcher.</p><h3>Don’t confuse absence with invisibility</h3><ul><li>not.toBeInTheDocument() ⇒ the node <strong>doesn’t exist</strong> in the DOM.</li><li>not.toBeVisible() ⇒ the node <strong>exists</strong> but is hidden (CSS or ARIA).</li></ul><p>Use the one that matches your intent:</p><pre>// Exists but hidden:<br>expect(screen.getByRole(&quot;dialog&quot;, { hidden: true })).not.toBeVisible();<br><br>// Truly gone:<br>expect(screen.queryByRole(&quot;dialog&quot;)).not.toBeInTheDocument();</pre><h3>When something should disappear after an action</h3><p>Animation or async state updates can make tests flaky if you assert too early. Tie the wait to the actual node.</p><pre>import { waitForElementToBeRemoved } from &quot;@testing-library/react&quot;;<br>import userEvent from &quot;@testing-library/user-event&quot;;<br><br>await userEvent.click(screen.getByRole(&quot;button&quot;, { name: /close/i }));<br><br>await waitForElementToBeRemoved(() =&gt;<br>  screen.queryByRole(&quot;dialog&quot;, { name: /settings/i })<br>);<br>expect(screen.queryByRole(&quot;dialog&quot;, { name: /settings/i })).not.toBeInTheDocument();</pre><p>Fallback if your component toggles visibility before unmounting:</p><pre>import { waitFor } from &quot;@testing-library/react&quot;;<br><br>await userEvent.keyboard(&quot;{Escape}&quot;);<br>await waitFor(() =&gt; {<br>  expect(screen.queryByRole(&quot;dialog&quot;)).not.toBeInTheDocument();<br>});</pre><h3>Prefer accessible queries; use data-testid only when needed</h3><p>Accessible queries mirror how users (and assistive tech) experience your UI and reduce brittle tests.</p><pre>// Better:<br>expect(screen.queryByRole(&quot;menu&quot;)).not.toBeInTheDocument();<br><br>// Acceptable fallback:<br>expect(screen.queryByTestId(&quot;user-menu&quot;)).not.toBeInTheDocument();</pre><h3>Common anti-patterns (and fixes)</h3><p><strong>getBy* for absence</strong></p><pre>// ❌ Throws before matcher<br>expect(screen.getByTestId(&quot;x&quot;)).not.toBeInTheDocument();</pre><pre>// ✅<br>expect(screen.queryByTestId(&quot;x&quot;)).not.toBeInTheDocument();</pre><p><strong>findBy* with negation (</strong><strong>await screen.findBy…; expect(...).not)</strong></p><p>This waits for something to appear, then negates it. Slow and misleading.<br> <strong>Use</strong> waitForElementToBeRemoved or waitFor when asserting disappearance.</p><p><strong>Array length checks</strong></p><pre>// ❌ Brittle &amp; throws when empty<br>expect(screen.getAllByRole(&quot;menu&quot;).length).toBe(0);</pre><pre>// ✅ Clear intent<br>expect(screen.queryByRole(&quot;menu&quot;)).not.toBeInTheDocument();</pre><p><strong>Using visibility to prove absence</strong></p><p>toBeVisible() tests CSS, not DOM presence. If the requirement is “it’s gone,” assert <strong>non-existence</strong>.</p><h3>Edge cases you should plan for</h3><ul><li><strong>Portals (modals, tooltips):</strong> Your queries still operate on document.body, but ensure your test environment renders portals like production. If you mount to a custom container, reflect that in setup.</li><li><strong>Transitions:</strong> Many UI libs keep nodes in the DOM during exit animations. First assert the transitional state (e.g., aria-hidden=&quot;true&quot; or toBeVisible() false), then wait for removal.</li><li><strong>Live regions / alerts:</strong> If a banner announces (e.g., role=&quot;alert&quot;) and should vanish, wait for removal rather than using arbitrary timeouts.</li><li><strong>Hidden content searches:</strong> If you intentionally need to include hidden nodes in a query, pass { hidden: true }—but don’t conflate this with absence.</li></ul><h3>A tiny decision guide</h3><ul><li><strong>Expect missing now?</strong><br> expect(screen.queryByRole(...)).not.toBeInTheDocument()</li><li><strong>Expect to disappear after user action?</strong><br> await waitForElementToBeRemoved(() =&gt; screen.queryByRole(...))</li><li><strong>Expect hidden but mounted?</strong><br> expect(screen.getByRole(..., { hidden: true })).not.toBeVisible()</li></ul><h3>Wrap-up</h3><ul><li>Presence ⇒ getBy*</li><li><strong>Absence ⇒ </strong><strong>queryBy* + </strong><strong>not.toBeInTheDocument()</strong></li><li>Disappearance ⇒ waitForElementToBeRemoved (or waitFor)</li><li>Prefer accessible queries; use TestIDs only when necessary</li><li>Don’t test CSS when you mean DOM</li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=749ad1c82482" width="1" height="1" alt=""><hr><p><a href="https://medium.com/javascript-render/stop-testing-ghosts-test-non-existence-with-react-testing-library-749ad1c82482">Stop Testing Ghosts: Test Non-Existence with React Testing Library</a> was originally published in <a href="https://medium.com/javascript-render">Javascript &amp; Render</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Analysis of Error Tracking & Performance Monitoring Tools]]></title>
            <link>https://medium.com/javascript-render/analysis-of-error-tracking-performance-monitoring-tools-aa2b9e3bdd20?source=rss----da9975776266---4</link>
            <guid isPermaLink="false">https://medium.com/p/aa2b9e3bdd20</guid>
            <category><![CDATA[sentry]]></category>
            <category><![CDATA[error-handling]]></category>
            <category><![CDATA[rollbar]]></category>
            <dc:creator><![CDATA[Gerardo Perrucci]]></dc:creator>
            <pubDate>Sat, 08 Nov 2025 08:35:45 GMT</pubDate>
            <atom:updated>2025-11-08T08:34:52.240Z</atom:updated>
            <content:encoded><![CDATA[<p>When evaluating alternatives to Sentry for error tracking and performance monitoring in React applications, several robust options are available. Below is a comparative analysis of some leading tools, highlighting their strengths and potential drawbacks.</p><h3>Comparative Analysis of Error Tracking &amp; Performance Monitoring Tools</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*1aPgIwpH4qEaJbO4VkZ6ug.png" /><figcaption>Comparative Analysis of Error Tracking &amp; Performance Monitoring Tools</figcaption></figure><h3>Tool Summaries</h3><ul><li><strong>Sentry</strong>: Offers robust error tracking with detailed stack traces and performance monitoring. Suitable for teams that prioritize deep error insights.</li><li><strong>Rollbar</strong>: Provides real-time error tracking with intelligent grouping, aiding in rapid issue resolution. Ideal for teams emphasizing quick error detection.</li><li><strong>Bugsnag</strong>: Focuses on application stability, offering real-time error tracking and stability scores. Beneficial for teams aiming to monitor and improve app stability.(<a href="https://stackshare.io/stackups/new-relic-vs-sentry?utm_source=chatgpt.com">StackShare</a>)</li><li><strong>New Relic</strong>: Delivers comprehensive application performance monitoring, including infrastructure and user experience insights. Best suited for enterprises needing full-stack observability.(<a href="https://www.walturn.com/insights/monitoring-flutter-apps-with-sentry-and-new-relic?utm_source=chatgpt.com">Walturn</a>)</li><li><strong>Datadog</strong>: Provides extensive observability features, including APM, infrastructure monitoring, and security. Suitable for organizations requiring a unified monitoring platform.(<a href="https://www.walturn.com/insights/monitoring-flutter-apps-with-sentry-and-new-relic?utm_source=chatgpt.com">Walturn</a>)</li><li><strong>Atatus</strong>: Offers an all-in-one monitoring solution with error tracking, APM, and real user monitoring. Ideal for teams seeking comprehensive insights in a single platform.(<a href="https://www.walturn.com/insights/monitoring-flutter-apps-with-sentry-and-new-relic?utm_source=chatgpt.com">Walturn</a>)</li><li><strong>GlitchTip</strong>: An open-source alternative compatible with Sentry SDKs, offering basic error tracking and performance monitoring. Suitable for organizations preferring self-hosted solutions.(<a href="https://betterstack.com/community/comparisons/error-tracking-tools/?utm_source=chatgpt.com">BetterStack</a>)</li></ul><h3>✅ Recommendation</h3><p>Considering the features and capabilities of the tools analyzed, <strong>Atatus</strong> emerges as a compelling choice for React applications. It offers a comprehensive suite of monitoring tools, including error tracking, performance monitoring, and real user monitoring, all within an easy-to-use platform. Its affordability and ease of setup make it accessible for teams of various sizes.</p><h3>References</h3><ul><li><a href="https://alerty.ai/blog/sentry-alternatives">15 Best Sentry Alternatives For Error Tracking &amp; Monitoring — Alerty</a></li><li><a href="https://medium.com/@squadcast/sentry-vs-bugsnag-choosing-the-right-error-monitoring-tool-f4a5b74f6b7d">Sentry vs Bugsnag: Choosing the Right Error Monitoring Tool</a></li><li><a href="https://signoz.io/comparisons/newrelic-vs-sentry/">New Relic vs Sentry — In-depth Comparison Guide [2025] | SigNoz</a></li><li><a href="https://betterstack.com/community/comparisons/error-tracking-tools/">11 Best Error Tracking Tools in 2025 | Better Stack Community</a></li><li><a href="https://stackshare.io/stackups/rollbar-vs-sentry">Rollbar vs Sentry | What are the differences? — StackShare</a></li><li><a href="https://blog.appsignal.com/2024/11/21/the-top-5-sentry-alternatives.html">The Top 5 Sentry Alternatives — AppSignal Blog</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=aa2b9e3bdd20" width="1" height="1" alt=""><hr><p><a href="https://medium.com/javascript-render/analysis-of-error-tracking-performance-monitoring-tools-aa2b9e3bdd20">Analysis of Error Tracking &amp; Performance Monitoring Tools</a> was originally published in <a href="https://medium.com/javascript-render">Javascript &amp; Render</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Creative Web Design with CSS mask-image in 3 Minutes]]></title>
            <link>https://medium.com/javascript-render/creative-web-design-with-css-mask-image-in-3-minutes-af1f21b7c032?source=rss----da9975776266---4</link>
            <guid isPermaLink="false">https://medium.com/p/af1f21b7c032</guid>
            <category><![CDATA[mask-images]]></category>
            <category><![CDATA[clip-path]]></category>
            <category><![CDATA[css]]></category>
            <dc:creator><![CDATA[Gerardo Perrucci]]></dc:creator>
            <pubDate>Sat, 08 Nov 2025 08:35:44 GMT</pubDate>
            <atom:updated>2025-11-08T08:34:49.953Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Spauyyt2FGHsmquiWYriYQ.png" /><figcaption>CSS mask-image</figcaption></figure><p>CSS mask-image is an exciting property that helps developers add creative visuals to websites without heavy graphic editing. Imagine shaping images, fading out text, or creating interactive visual effects directly through CSS—this is what mask-image offers.</p><p>You can read the full article: <a href="https://www.gperrucci.com/en/blog/css/mask-image">CSS `mask-image`: In-Depth Guide to Usage and Best Practices</a></p><pre>.fade-image {<br>  width: 300px;<br>  height: 400px;<br>  background: url(https://medium.com/&quot;./food.jpg&quot;) center/cover no-repeat;<br>  /* Apply a top-to-bottom gradient mask: fully opaque at top, transparent at bottom */<br>  mask-image: linear-gradient(to bottom, black 40%, transparent 100%);<br>  mask-size: 100% 100%;<br>  mask-repeat: no-repeat;<br>}</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*LW1-zCXsTdW-fU0wO5HVNA.png" /></figure><h3>What Exactly is mask-image?</h3><p>mask-image lets you control the visibility of parts of an element using images or gradients as masks. Areas covered by opaque mask pixels remain visible, while transparent areas become invisible. It&#39;s ideal for creating complex shapes or smooth transparency effects.</p><h3>Why Use mask-image over clip-path?</h3><p>While clip-path is great for simple shapes with sharp edges, mask-image shines when you need more sophisticated visual effects, like soft fades and intricate shapes. If you&#39;re creating something like a star-shaped photo or a gradient fade effect, mask-image is your tool.</p><h3>Quick Example (React)</h3><p>Here’s a practical example using React to toggle between different mask styles:Browser Compatibility</p><p>Modern browsers widely support CSS masks, but always include the WebKit prefix (-webkit-mask-image) to ensure compatibility with Safari and older browsers.</p><pre>import React, { useState } from &quot;react&quot;;<br>import &quot;./styles.css&quot;;<br><br>function App() {<br>  const [maskShape, setMaskShape] = useState(&quot;circle&quot;);<br><br>  const handleShapeChange = (shape) =&gt; {<br>    setMaskShape(shape);<br>  };<br><br>  return (<br>    &lt;div className=&quot;App&quot;&gt;<br>      &lt;div className=&quot;button-container&quot;&gt;<br>        &lt;button onClick={() =&gt; handleShapeChange(&quot;circle&quot;)}&gt;Circle&lt;/button&gt;<br>        &lt;button onClick={() =&gt; handleShapeChange(&quot;rectangle&quot;)}&gt;<br>          Rectangle<br>        &lt;/button&gt;<br>        &lt;button onClick={() =&gt; handleShapeChange(&quot;triangle&quot;)}&gt;Triangle&lt;/button&gt;<br>      &lt;/div&gt;<br>      &lt;div className=&quot;image-container&quot;&gt;<br>        &lt;svg width=&quot;0&quot; height=&quot;0&quot;&gt;<br>          &lt;defs&gt;<br>            &lt;mask id=&quot;circle-mask&quot; maskUnits=&quot;objectBoundingBox&quot;&gt;<br>              &lt;circle cx=&quot;400&quot; cy=&quot;50&quot; r=&quot;120&quot; fill=&quot;#FFFFFF&quot; /&gt;<br>              &lt;circle cx=&quot;100&quot; cy=&quot;50&quot; r=&quot;120&quot; fill=&quot;#FFFFFF&quot; /&gt;<br>            &lt;/mask&gt;<br>            &lt;mask id=&quot;rectangle-mask&quot; maskUnits=&quot;objectBoundingBox&quot;&gt;<br>              &lt;rect width=&quot;130&quot; height=&quot;130&quot; fill=&quot;#FFFFFF&quot; /&gt;<br>              &lt;rect width=&quot;130&quot; height=&quot;130&quot; y=&quot;120&quot; x=&quot;120&quot; fill=&quot;#FFFFFF&quot; /&gt;<br>              &lt;rect width=&quot;130&quot; height=&quot;130&quot; y=&quot;0&quot; x=&quot;220&quot; fill=&quot;#FFFFFF&quot; /&gt;<br>              &lt;rect width=&quot;130&quot; height=&quot;130&quot; y=&quot;120&quot; x=&quot;320&quot; fill=&quot;#FFFFFF&quot; /&gt;<br>            &lt;/mask&gt;<br>            &lt;mask id=&quot;triangle-mask&quot; maskUnits=&quot;objectBoundingBox&quot;&gt;<br>              &lt;polygon points=&quot;200,0 0,85 200,205&quot; fill=&quot;#FFFFFF&quot; /&gt;<br>              &lt;polygon points=&quot;400,0 200,85 400,205&quot; fill=&quot;#FFFFFF&quot; /&gt;<br>            &lt;/mask&gt;<br>          &lt;/defs&gt;<br>        &lt;/svg&gt;<br>        &lt;img<br>          src=&quot;chessboard.webp&quot;<br>          alt=&quot;Masked&quot;<br>          className={`masked-image ${maskShape}`}<br>        /&gt;<br>      &lt;/div&gt;<br>    &lt;/div&gt;<br>  );<br>}<br><br>export default App;</pre><pre>.App {<br>  text-align: center;<br>}<br><br>.button-container {<br>  margin: 20px;<br>}<br><br>.image-container {<br>  display: inline-block;<br>  position: relative;<br>}<br><br>.masked-image {<br>  width: 500px;<br>  transition: mask-image 0.5s ease-in-out;<br>}<br><br>.masked-image.circle {<br>  -webkit-mask-image: url(#circle-mask);<br>  mask-image: url(#circle-mask);<br>}<br><br>.masked-image.rectangle {<br>  -webkit-mask-image: url(#rectangle-mask);<br>  mask-image: url(#rectangle-mask);<br>}<br><br>.masked-image.triangle {<br>  -webkit-mask-image: url(#triangle-mask);<br>  mask-image: url(#triangle-mask);<br>}</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/692/1*1Ym61FTF33WxH1RIQyZqTQ.gif" /></figure><h3>Dive Deeper</h3><p>For an extensive exploration, practical comparisons, interactive examples, and best practices (including performance tips), check out <a href="https://www.gperrucci.com/en/blog/css/mask-image">the complete CSS </a><a href="https://www.gperrucci.com/en/blog/css/mask-image">mask-image guide</a>.</p><h3>References &amp; Documentation</h3><ul><li><a href="https://developer.mozilla.org/en-US/docs/Web/CSS/mask-image">MDN Web Docs</a></li><li><a href="https://drafts.fxtf.org/css-masking-1/">W3C CSS Masking Specification</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=af1f21b7c032" width="1" height="1" alt=""><hr><p><a href="https://medium.com/javascript-render/creative-web-design-with-css-mask-image-in-3-minutes-af1f21b7c032">Creative Web Design with CSS mask-image in 3 Minutes</a> was originally published in <a href="https://medium.com/javascript-render">Javascript &amp; Render</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[JavaScript Junior Interview Questions Explained]]></title>
            <link>https://medium.com/javascript-render/javascript-junior-interview-questions-explained-7a8f1e7141bf?source=rss----da9975776266---4</link>
            <guid isPermaLink="false">https://medium.com/p/7a8f1e7141bf</guid>
            <category><![CDATA[interview]]></category>
            <category><![CDATA[javascript-questions]]></category>
            <category><![CDATA[javascript-jr]]></category>
            <dc:creator><![CDATA[Gerardo Perrucci]]></dc:creator>
            <pubDate>Tue, 14 Oct 2025 10:03:16 GMT</pubDate>
            <atom:updated>2025-10-14T10:03:12.239Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*AvtZYr0zmDxa9T95F7WU2g.png" /></figure><p>Understanding core JavaScript concepts is essential for acing a junior developer interview. This article covers common interview questions, providing explanations and code snippets to illustrate each concept.</p><h3>1. What is the difference between var, let, and const?</h3><p>var declares a function-scoped or globally-scoped variable, while let and const are block-scoped. const also prevents reassignment of the variable. Variables declared with var can be re-declared in the same scope and attach to the global object when declared globally, which may cause unexpected behavior. In contrast, let and const throw errors when re-declared in the same block and remain block-scoped.</p><pre>var globalVar = &#39;old&#39;;<br>if (true) {<br>  var globalVar = &#39;new&#39;; // Re-declared in the same scope<br>  let blockLet = &#39;block&#39;;<br>  const blockConst = &#39;constant&#39;;<br>}<br>console.log(globalVar); // &#39;new&#39;</pre><p>Sources:</p><ul><li><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/var">MDN: </a><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/var">var</a></li><li><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let">MDN: </a><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let">let</a></li><li><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/const">MDN: </a><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/const">const</a></li></ul><h3>2. Explain hoisting.</h3><p>Hoisting is JavaScript’s behavior of moving declarations to the top of their scope before code execution. Function declarations and variable declarations using var are hoisted, whereas let and const are not initialized until the code executes. During hoisting, declarations are moved but assignments stay in place. Calling a variable before assignment returns undefined. Only function declarations are fully hoisted, while function expressions and arrow functions behave like variables.</p><pre>console.log(a); // undefined<br>var a = 5;</pre><p>Source:</p><ul><li><a href="https://developer.mozilla.org/en-US/docs/Glossary/Hoisting">MDN: Hoisting</a></li></ul><h3>3. What are closures?</h3><p>A closure occurs when a function retains access to its lexical scope even when executed outside that scope. Closures are commonly used for data privacy and creating factory functions. This ability to remember the surrounding scope allows functions to maintain private variables, enabling patterns like once functions, memoization, and currying.</p><pre>function makeCounter() {<br>  let count = 0;<br>  return function () {<br>    count++;<br>    return count;<br>  };<br>}</pre><pre>const counter = makeCounter();<br>console.log(counter()); // 1<br>console.log(counter()); // 2</pre><p>Source:</p><ul><li><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures">MDN: Closures</a></li></ul><h3>4. What is event bubbling?</h3><p>Event bubbling is the propagation of events from the deepest target element upward through the DOM tree. You can stop propagation with event.stopPropagation(). Events first trigger on the innermost element, then bubble up to ancestors unless the handler stops propagation. The opposite phase is event capturing, which triggers handlers from the root down before the bubbling phase.</p><pre>&lt;div id=&quot;outer&quot;&gt;<br>  &lt;button id=&quot;inner&quot;&gt;Click me&lt;/button&gt;<br>&lt;/div&gt;</pre><pre>const outer = document.getElementById(&#39;outer&#39;);<br>const inner = document.getElementById(&#39;inner&#39;);</pre><pre>outer.addEventListener(&#39;click&#39;, () =&gt; console.log(&#39;Outer clicked&#39;));<br>inner.addEventListener(&#39;click&#39;, e =&gt; {<br>  console.log(&#39;Inner clicked&#39;);<br>  // e.stopPropagation(); // Uncomment to stop bubbling<br>});</pre><p>Source:</p><ul><li><a href="https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Building_blocks/Events#event_bubbling">MDN: Event Bubbling</a></li></ul><h3>5. How do promises and async/await handle asynchronous code?</h3><p>Promises represent the eventual result of an asynchronous operation. async/await simplifies promise-based code by allowing asynchronous logic to be written in a synchronous style. They provide methods like then and catch for chaining actions once the asynchronous task finishes or fails. When used with async functions, you can handle errors using try/catch, making asynchronous code easier to read.</p><pre>function fetchData() {<br>  return new Promise(resolve =&gt; {<br>    setTimeout(() =&gt; resolve(&#39;Done&#39;), 1000);<br>  });<br>}</pre><pre>async function run() {<br>  const result = await fetchData();<br>  console.log(result); // &#39;Done&#39;<br>}</pre><pre>run();</pre><p>Sources:</p><ul><li><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise">MDN: Promise</a></li><li><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function">MDN: async function</a></li></ul><h3>6. What are the primitive data types in JavaScript?</h3><p>JavaScript has seven primitive data types: string, number, bigint, boolean, undefined, symbol, and null. These types are immutable, meaning their values cannot be changed once created. Non-primitive types like objects and arrays are mutable and passed by reference.</p><pre>const name = &#39;Jane&#39;;<br>const age = 30;<br>const big = 123n;<br>const active = true;<br>let unknown;<br>const id = Symbol(&#39;id&#39;);<br>const empty = null;</pre><p>Source:</p><ul><li><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures">MDN: Data types</a></li></ul><h3>7. How does this work in JavaScript?</h3><p>this is determined by how a function is called. In the global context, this refers to the global object (window in browsers). Inside an object method, this refers to that object. Arrow functions do not have their own this; they inherit it from the surrounding scope. Methods like call, apply, and bind can explicitly set the this value.</p><pre>const obj = {<br>  value: 42,<br>  regular() {<br>    console.log(this.value); // 42<br>  },<br>  arrow: () =&gt; {<br>    console.log(this.value); // undefined in most cases<br>  },<br>};</pre><pre>obj.regular();<br>obj.arrow();</pre><p>Source:</p><ul><li><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this">MDN: </a><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this">this</a></li></ul><h3>8. What is the difference between == and ===?</h3><p>== compares values after performing type coercion, which can lead to unexpected results. === checks both value and type without coercion, making it the preferred choice for most comparisons.</p><pre>0 == &#39;0&#39;; // true<br>0 === &#39;0&#39;; // false</pre><p>Source:</p><ul><li><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Equality_comparisons_and_sameness">MDN: Equality comparisons</a></li></ul><h3>9. Explain the event loop in JavaScript.</h3><p>The event loop handles asynchronous operations by coordinating the call stack and task queues. When the stack is empty, the loop processes messages from the queue, executing callbacks. Promises use the microtask queue, which has priority over the macrotask queue used by setTimeout.</p><pre>console.log(&#39;start&#39;);<br>setTimeout(() =&gt; console.log(&#39;timeout&#39;), 0);<br>Promise.resolve().then(() =&gt; console.log(&#39;promise&#39;));<br>console.log(&#39;end&#39;);<br>// Output: start, end, promise, timeout</pre><p>Source:</p><ul><li><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Event_loop">MDN: Event loop</a></li></ul><h3>10. What is destructuring assignment?</h3><p>Destructuring allows extracting values from arrays or properties from objects into distinct variables, making code more concise and readable.</p><pre>const [first, second] = [1, 2];<br>const { name, age } = { name: &#39;Jane&#39;, age: 30 };</pre><p>Source:</p><ul><li><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment">MDN: Destructuring assignment</a></li></ul><h3>References</h3><ul><li><a href="https://developer.mozilla.org/en-US/">MDN Web Docs</a> — Comprehensive JavaScript documentation.</li><li><a href="https://javascript.info/">JavaScript.info</a> — Tutorials and explanations on core JavaScript concepts.</li><li><a href="https://javascript.info/">JavaScript.info</a> — Tutorials and explanations on core JavaScript concepts.</li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=7a8f1e7141bf" width="1" height="1" alt=""><hr><p><a href="https://medium.com/javascript-render/javascript-junior-interview-questions-explained-7a8f1e7141bf">JavaScript Junior Interview Questions Explained</a> was originally published in <a href="https://medium.com/javascript-render">Javascript &amp; Render</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
    </channel>
</rss>