<?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[Stories by Alex Savelyev on Medium]]></title>
        <description><![CDATA[Stories by Alex Savelyev on Medium]]></description>
        <link>https://medium.com/@alexdln?source=rss-f9cb10da019e------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*HYZdqANsFVLWWzFkn9hsCg.jpeg</url>
            <title>Stories by Alex Savelyev on Medium</title>
            <link>https://medium.com/@alexdln?source=rss-f9cb10da019e------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Thu, 04 Jun 2026 00:18:52 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@alexdln/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[Next.js v15 — What’s new under the hood]]></title>
            <link>https://alexdln.medium.com/next-js-v15-reflecting-on-mistakes-455cf606fc64?source=rss-f9cb10da019e------2</link>
            <guid isPermaLink="false">https://medium.com/p/455cf606fc64</guid>
            <category><![CDATA[web-development]]></category>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[nextjs]]></category>
            <category><![CDATA[front-end-development]]></category>
            <category><![CDATA[react]]></category>
            <dc:creator><![CDATA[Alex Savelyev]]></dc:creator>
            <pubDate>Fri, 25 Oct 2024 09:37:35 GMT</pubDate>
            <atom:updated>2026-03-02T13:55:41.310Z</atom:updated>
            <content:encoded><![CDATA[<h3>Next.js v15 — What’s new under the hood</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*0fwiZF4WCFJikQe1I4_w5Q.png" /><figcaption>Source: <a href="https://nextjs.org/">nextjs.org</a></figcaption></figure><p>Hello! This is another article about next.js. And finally, about the new version! Each release is a set of new, interesting, and controversial features. This version will be no exception. However, new version is interesting not so much for its new functionality, but for the change in priorities and organization in next.js. And yes, as you may have guessed from the title, a significant part of this release is valuable for reflecting on previous mistakes.</p><p>I’ve been working with next.js since around version 8. All this time I’ve been watching its development with interest (<em>sometimes not without disappointment</em>). Recently, I’ve published a series of articles about struggling with the new App Router — “<a href="https://medium.com/@vordgi/next-js-app-router-experience-of-use-path-to-the-future-or-wrong-turn-277dde4369a6">Next.js App Router. A path to the future or a wrong turn</a>”, “<a href="https://medium.com/@vordgi/caching-in-next-js-gift-or-curse-095a35b9723a">Next.js caching. A gift or a curse</a>”, “<a href="https://medium.com/@vordgi/how-i-revisited-i18n-47470ef71c91">More libraries for the god of libraries or how I rethought i18n</a>”. All of these were a result of very weak development of ideas and capabilities in previous versions of next.js. And because of this, my interest in the new version has only grown. Along with that, there’s a desire to understand the vector of changes in the framework.</p><p>In this article, I won’t dwell on what App Router or server components are — these are described in detail in previous articles. We’ll focus only on the new version and only on the new changes.</p><p>Note: <em>The article reflects the most interesting changes from the author’s perspective. They differ from the official list, as the author selected them from commits and PRs in the framework.</em></p><h3>Next.js v15 Release</h3><p>First, a bit about changes in the internal development processes of next.js. For the first time, the framework team has published a release candidate (<em>RC version</em>). Obviously, they did this due to the React.js team’s decision to publish <a href="https://react.dev/blog/2024/04/25/react-19">React v19 RC</a>.</p><p>Usually, the next.js team in their stable releases calmly uses react from the “Canary” release branch (<em>this branch is considered stable and recommended for use by frameworks</em>). This time, however, they decided to do things differently (<em>spoiler alert — not in vain</em>).</p><p>The plan for both teams was simple — publish a pre-release version, let the community check for issues, and in a couple of weeks publish a full release.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*MyO0DWJ5EEhDwp5XQZQc_w.png" /><figcaption>Tweet from React.js core-team developer <a href="https://x.com/acdlite/status/1797668537349328923">https://x.com/acdlite/status/1797668537349328923</a></figcaption></figure><p>It’s been over six months since the release candidate of React.js was released, but the stable version still hasn’t been published. The delay in releasing the stable version of React.js has impacted next.js’s plans as well. Therefore, contrary to tradition, they published a total of 15 additional patch versions while already working on the 15th version (<em>usually 3–5 patches and then a release</em>). What’s noteworthy here is that these patch versions didn’t include all accumulated changes, but only addressed critical issues, which also deviates from next.js’s usual processes.</p><p><em>The basic release process in next.js is that everything merges into the canary branch, and then, at some point, this branch is published as a stable release.</em></p><p>However, as a result, the next.js team decided to decouple from the React.js release and publish a stable version of the framework before the stable version of React.js is released.</p><h3>Documentation Versioning</h3><p>Another very useful organizational change. Finally, it’s possible to view different versions of the documentation. Here’s why this is so important:</p><p>Firstly, updating next.js can often be quite a challenging task due to major changes. In fact, this is why there are still over 2 million downloads for version 12 and over 4 million for version 13 monthly (<em>to be fair, version 14 has over 20 million downloads</em>).</p><p>Consequently, users of previous versions need documentation specific to their version, as the new one might be rewritten for a half.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*vPRNQfRYTMN3tUdFa9UQqw.png" /><figcaption>Next.js documentation versioning — <a href="http://nextjs.org/docs">nextjs.org/docs</a></figcaption></figure><p>Another problem is that Next.js essentially uses a single channel. Documentation changes are also made to it. Therefore, descriptions of changes from canary versions immediately appeared in the main documentation. Now they are displayed under the “canary” section.</p><h3>React usage</h3><p>At the beginning, I mentioned that Next.js is currently using the RC version of React.js. But in reality, this is not quite true, or rather not entirely true. In fact, Next.js is currently using two React.js configurations: the 19th canary version for App Router and the 18th version for Pages Router.</p><p>Interestingly, at one moment they wanted to include the 19th version for Pages Router as well, but then rolled back these changes. Now, full support for React.js version 19 is promised after the release of its stable version.</p><p>Along with this, the new version will have several useful improvements for server actions functions (<em>yes, </em><a href="https://19.react.dev/reference/rsc/server-functions"><em>the React team renamed them</em></a>):</p><ul><li>Optimization of weight and performance;</li><li>Improved error handling;</li><li>Fixed revalidation and redirects from server functions.</li></ul><p>I suppose I’ll include Next.js’s new feature in this section as well — the <a href="https://nextjs.org/docs/canary/app/api-reference/components/form">Form component</a>. Overall, it’s the familiar form from react-dom, but with some improvements. This component is primarily needed if successful form submission involves navigating to another page. For the next page, the loading.tsx and layout.tsx abstractions will be pre-loaded.</p><pre>import Form from &#39;next/form&#39;<br> <br>export default function Page() {<br>  return (<br>    &lt;Form action=&quot;/search&quot;&gt;;<br>      {/* On submission, the input value will be appended to <br>          the URL, e.g. /search?query=abc */}<br>      &lt;input name=&quot;query&quot; /&gt;;<br>      &lt;button type=&quot;submit&quot;&gt;Submit&lt;/button&gt;;<br>    &lt;/Form&gt;;<br>  )<br>}</pre><h3>Developer Experience (DX)</h3><p>When talking about Next.js, we can’t ignore the developer experience. In addition to the standard “Faster, Higher, Stronger” (<em>which we’ll also discuss, but a bit later</em>), several useful improvements have been released.</p><p>Long-awaited support for the latest ESLint. Next.js didn’t support ESLint v9 until now. This is despite the fact that both eslint itself (v8) and some of its subdependencies are already marked as deprecated. This resulted in an unpleasant situation where projects were essentially forced to keep deprecated packages.</p><p>The error interface has been slightly improved (<em>which in Next.js is already clear and convenient</em>):</p><ul><li>Added a button to copy the stack trace;</li><li>Added the ability to open the error source in the editor at a specific line.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*DvMfqdkPAJ1ns9Rks0PUzw.png" /><figcaption>Example of copying the error stack in next.js</figcaption></figure><p>A “Static Indicator” has been added — an element in the corner of the page showing that the page has been built in static mode. Overall, it’s a minor thing, but it’s amusing that they included it in the key changes as something new. The indicator for a “pre-built” page has been around since roughly version 8 (<em>2019</em>) and here, essentially, they’ve just slightly updated it and adapted it for the App Router.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*UukIs8paIPpNAcExPTm7yA.png" /></figure><p>A directory with debugging information has also been added — .next/diagnostics. It will contain information about the build process and all errors that occur. It&#39;s not yet clear if this will be useful in daily use, but it will certainly be used when troubleshooting issues with Vercel devrels (<em>yes, they sometimes help to solve problems</em>).</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*WxtyqbYzvl9yelMOjQ72qg.png" /><figcaption>Next.js team’s response to a <a href="https://x.com/darshansrc/status/1797339543571755425">tweet about slow project build</a></figcaption></figure><h3>Changes in the Build Process</h3><p>After discussing DX, it’s worth talking about the build process. And along with it, Turbopack.</p><h3>Turbopack</h3><p>And the biggest news in this area. Turbopack is now fully completed for development mode! “100% of existing tests passed without errors with Turbopack”</p><p>Now the Turbo team is working on the production version, gradually going through the tests and refining them (currently about 96% complete)</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*b0oBo-UcBZxTnQ_--O7JvA.png" /><figcaption>Example changelog section in next.js</figcaption></figure><p>Turbopack also adds new capabilities:</p><ul><li>Setting a memory limit for builds with Turbopack;</li><li>Tree Shaking (<em>removal of unused code</em>).</li></ul><pre>const nextConfig = {<br>  experimental: {<br>    turbo: {<br>      treeShaking: true,<br>      memoryLimit: 1024 * 1024 * 512 // in bytes / 512MB<br>    },<br>  },<br>}</pre><p>These and other improvements in Turbopack “reduced memory usage by 25–30%” and also “accelerated the build of heavy pages by 30–50%”.</p><h3>Other</h3><p>Significant style issues have been fixed. In version 14, situations often arose where the order of styles was broken during navigation, causing style A to become higher than style B, than vice versa. This changed their priority and consequently, elements looked different.</p><p>The next long-awaited improvement. Now the configuration file can be written in TypeScript — next.config.ts</p><pre>import type { NextConfig } from &#39;next&#39;;<br> <br>const nextConfig: NextConfig = {<br>  /* config options here */<br>};<br> <br>export default nextConfig;</pre><p>Another interesting update is retrying attempts for static page builds. This means if a page fails at build time (<em>for example, due to internet problems</em>) — it will try to build again.</p><pre>const nextConfig = {<br>  experimental: {<br>    staticGenerationRetryCount: 3,<br>  },<br>}</pre><p>And to conclude this section, a functionality highly desired by the community — the ability to specify the path to additional files for building. With this option, you can, for example, specify that files are located not in the app directory, but in directories like modules/main, modules/invoices.</p><p>However, at the moment, they have only added it for internal team purposes. And in this version, they definitely won’t present it. Going forward, it will either be used for Vercel needs, or they will test it and present it in the next release.</p><h3>Changes in the Framework API</h3><p>The most painful part of Next.js updates — API changes. And in this version, there are also breaking updates.</p><p>Several internal framework APIs have become asynchronous — cookies, headers, params and searchParams (<em>so-called Dynamic APIs</em>).</p><pre>import { cookies } from &#39;next/headers&#39;;<br> <br>export async function AdminPanel() {<br>  const cookieStore = await cookies();<br>  const token = cookieStore.get(&#39;token&#39;);<br>  // ...<br>}</pre><p>It’s a major change, but the Next.js team promises that all this functionality can be updated automatically by calling their codemod:</p><p>npx @next/codemod@canary next-async-request-api .</p><p>Another change, but probably not relevant to many. The keys geo and ip have been removed from NextRequest (<em>used in middleware and API routes</em>). Essentially, this functionality only worked in Vercel, while in other places developers made their own methods. For Vercel, this functionality will be moved to the <a href="https://vercel.com/docs/functions/vercel-functions-package">@vercel/functions</a> package</p><p>And a few more updates:</p><ul><li>In revalidateTag, you can now pass multiple tags at once;</li><li>Keys images.remotePatterns.search and images.localPatterns have been added to the configuration for next/image. These allow better control over address restrictions for image compression.</li></ul><pre>const nextConfig = {<br>  images: {<br>    localPatterns: [<br>      {<br>        pathname: &#39;/assets/images/**&#39;,<br>        search: &#39;v=1&#39;,<br>      },<br>    ],<br>  },<br>}</pre><h3>Caching</h3><p>In my personal opinion, this is where the most important changes for Next.js have occurred. And the biggest news is — <strong>Caching is now disabled by default</strong>! <em>I won’t go into detail about caching problems, this was largely covered in the article “</em><a href="https://medium.com/@vordgi/caching-in-next-js-gift-or-curse-095a35b9723a"><em>Next.js Caching. Gift or Curse</em></a><em>”.</em></p><p>Let’s go through all the main changes in caching:</p><ul><li>Specifically, fetch now uses the no-store value by default instead of force-cache;</li><li>API routes now work in force-dynamic mode by default (<em>previously the default was force-static, meaning they were compiled into a static response during build time [if dynamic APIs were not used on the page]</em>);</li><li>Caching in the client router has also been disabled. Previously, if a client visited a page within a route — it was cached on the client and remained in that state until the page was reloaded. Now, the current page will be loaded each time. This functionality can be reconfigured through next.config.js:</li></ul><pre>const nextConfig = {<br>  experimental: {<br>    staleTimes: {<br>      dynamic: 30 // defaults to 0<br>    },<br>  },<br>}</pre><ul><li>Moreover, even if client-side caching is enabled — it will apparently be updated at the right moment. Specifically, if the cache of an enabled page on the server expires.</li><li>Server components are now cached in development mode. This makes updates in development happen faster. The cache can be cleared simply by reloading the page. You can also completely disable this functionality through next.config.js:</li></ul><pre>const nextConfig = {<br>  experimental: {<br>    serverComponentsHmrCache: false, // defaults to true<br>  },<br>}</pre><ul><li>You can now manage the “Cache-Control” header. Previously, it was always rigidly overwritten with Next.js’s internal values. This caused artifacts with caching through CDN;</li><li>next/dynamic caches modules and reuses them, rather than loading them again each time;</li></ul><p>That’s regarding the “historical misunderstandings”. New APIs will also appear in Next.js. Namely, the so-called Dynamic I/O. It hasn&#39;t been written about anywhere yet, so the following will be the author&#39;s guesses based on the changes.</p><p>Dynamic I/O appears to be an advanced mode of dynamic building. Something like PPR (<em>Partial Prerendering</em>), or more precisely, its complement. In short, Partial Prerendering is a page building mode where most elements are built at build time and cached, while individual elements are built for each request.</p><p>So, dynamic I/O [probably] finalizes the architecture for this logic. It expands caching capabilities so that it can be enabled and disabled precisely depending on the mode and place of use (<em>whether in a &quot;dynamic&quot; block or not</em>).</p><pre>const nextConfig = {<br>  experimental: {<br>    dynamicIO: true, // defaults to false<br>  },<br>}</pre><p>Along with this, the &quot;use cache&quot; directive is added. It will be available in nodejs and edge runtimes and, apparently, in all server segments and abstractions. By specifying this directive at the top of a function or a module exporting a function - its result will be cached. The directive will only be available when dynamicIO is enabled.</p><pre>async function loadAndFormatData(page) {<br>  &quot;use cache&quot;<br>  ...<br>}</pre><p>Also, specifically for use cache, methods cacheLife and cacheTag are added</p><pre>export { unstable_cacheLife } from &#39;next/cache&#39;<br>export { unstable_cacheTag } from &#39;next/cache&#39;<br><br>async function loadAndFormatData(page) {<br>  &quot;use cache&quot;<br>  unstable_cacheLife(&#39;frequent&#39;);<br>  // or<br>  unstable_cacheTag(page, &#39;pages&#39;);<br>  ...<br>}</pre><p>cacheTag will be used for revalidation using revalidateTag, and cacheLife will set the cache lifetime. For the cacheLife value, you&#39;ll need to use one of the preset values. Several options will be available out of the box (&quot;seconds&quot;, &quot;minutes&quot;, &quot;hours&quot;, &quot;days&quot;, &quot;weeks&quot;, &quot;max&quot;), additional ones can be specified in next.config.js:</p><pre>const nextConfig = {<br>  experimental: {<br>    cacheLife?: {<br>      [profile: string]: {<br>        // How long the client can cache a value without checking with the server.<br>        stale?: number<br>        // How frequently you want the cache to refresh on the server.<br>        // Stale values may be served while revalidating.<br>        revalidate?: number<br>        // In the worst case scenario, where you haven&#39;t had traffic in a while,<br>        // how stale can a value be until you prefer deopting to dynamic.<br>        // Must be longer than revalidate.<br>        expire?: number<br>      }<br>    }<br>  }<br>}</pre><h3>Partial Prerendering (PPR)</h3><p>Probably the main feature of the next release. As mentioned earlier, PPR is a page building mode where most elements are assembled at build time and cached, while individual elements are assembled for each request. At the same time, the pre-built part is immediately sent to the client, while the rest is loaded dynamically.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*0fwiZF4WCFJikQe1I4_w5Q.png" /><figcaption>How Partial Prerendering works</figcaption></figure><p>The functionality itself was introduced six months ago in the release candidate as an experimental API. This API will remain in this state, and we will likely see it as stable only in version 16 (<em>which is good, as major functionality often transitioned to stable within six months to a year</em>).</p><p>Regarding the changes. As mentioned earlier, it primarily updated the working principles. However, from the perspective of using PPR, this hardly affected anything. At the same time, it received several improvements:</p><p>Previously, there was just a flag in the config, but now to enable PPR, you need to specify ‘incremental’. This is apparently done to make the logic more transparent — content can be cached by developers even in PPR, and to update it, you need to call revalidate methods.</p><pre>const nextConfig = {<br>  experimental: {<br>    ppr: &#39;incremental&#39;,<br>  },<br>}</pre><p>Also, previously PPR was launched for the entire project, but now it needs to be enabled for each segment (layout or page):</p><pre>export const experimental_ppr = true</pre><p>Another change is Partial Fallback Prerendering (PFPR). It’s precisely due to this improvement that the pre-built part is immediately sent to the client, while the rest is loaded dynamically. In place of dynamic elements, a callback component is shown during this time.</p><pre>import { Suspense } from &quot;react&quot;<br>import { StaticComponent, DynamicComponent } from &quot;@/app/ui&quot;<br> <br>export const experimental_ppr = true<br> <br>export default function Page() {<br>  return {<br>    &lt;&gt;<br>      &lt;StaticComponent /&gt;<br>      &lt;Suspense fallback={...}&gt;<br>        &lt;DynamicComponent /&gt;<br>      &lt;/Suspense&gt;<br>    &lt;/&gt;<br>  };<br>}</pre><h3>Instrumentation</h3><p>Instrumentation is marked as a stable API. The instrumentation file allows users to hook into the lifecycle of the Next.js server. It works across the entire application (<em>including all segments of Pages Router and App Router</em>).</p><p>Currently, instrumentation supports the following hooks:</p><p>register - called once when initializing the Next.js server. It can be used for integration with observability libraries (OpenTelemetry, datadog) or for project-specific tasks.</p><p>onRequestError - a new hook that is called for all server errors. It can be used for integrations with error tracking libraries (Sentry).</p><pre>export async function onRequestError(err, request, context) {<br>  await fetch(&#39;https://...&#39;, {<br>    method: &#39;POST&#39;,<br>    body: JSON.stringify({ message: err.message, request, context }),<br>    headers: { &#39;Content-Type&#39;: &#39;application/json&#39; },<br>  });<br>}<br> <br>export async function register() {<br>  // init your favorite observability provider SDK<br>}</pre><h3>Interceptor</h3><p>Interceptor, also known as route-level middleware. It’s something like a full-fledged [already existing] middleware, but unlike the latter:</p><ul><li>It can work in the Node.js runtime;</li><li>It works on the server (<em>which means it has access to the environment and unified cache</em>);</li><li>It can be added multiple times and is inherited in nesting (<em>similar to how middleware worked when it was in beta version</em>);</li><li>It also works for server functions.</li></ul><p>Moreover, when creating an interceptor file, all pages below in the tree become dynamic.</p><pre>import { auth } from &#39;@/auth&#39;;<br>import { redirect } from &#39;next/navigation&#39;;<br><br>const signInPathname = &#39;/dashboard/sign-in&#39;;<br><br>export default async function intercept(request: NextRequest): Promise&lt;void&gt; {<br>  // This will also seed React&#39;s cache, so that the session is already<br>  // available when the `auth` function is called in server components.<br>  const session = await auth();<br>  if (!session &amp;&amp; request.nextUrl.pathname !== signInPathname) {<br>    redirect(signInPathname);<br>  }<br>}<br><br>// lib/auth.ts<br>import { cache } from &#39;react&#39;;<br>export const auth = cache(async () =&gt; {<br>  // read session cookie from `cookies()`<br>  // use session cookie to read user from database<br>})</pre><p>Speaking of Vercel, middleware will now be effective as a primary simple check at the CDN level (<em>thus, for example, immediately returning redirects if the request is not allowed</em>), while interceptors will work on the server, performing full-fledged checks and complex operations.</p><p>In self-hosting, however, such a division will apparently be less effective (<em>since both abstractions work on the server</em>). It may be sufficient to use only interceptors.</p><h3>Conclusions</h3><p>Overwriting fetch, aggressive caching, numerous bugs, and ignoring community requests. The Next.js team made erroneous decisions, rushed releases, and held onto their views despite community feedback. It took almost a year to recognize the problems. And only now, finally, there’s a sense that the framework is once again addressing community issues.</p><p>On the other hand, there are other frameworks. A year ago, at the React.js presentation, it seemed that all frameworks would soon be on par with Next.js. React started mentioning Next.js less frequently as the main tool, frameworks were showcasing upcoming build systems, support for server components and functions, and a series of global changes and integrations. Time has passed, and essentially, none of them have reached that point yet.</p><p>Of course, final conclusions can only be drawn after some time, but for now, it feels like the changes in React.js, instead of the expected leveling of frameworks, have led to even greater dominance of Next.js and a wider divergence between frameworks (<em>since the implementation of server components and actions was left to the discretion of the frameworks</em>).</p><p>At the same time, OpenAI switched to Remix (<em>“due to its greater stability and convenience”</em>):</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*zC2tNRlHNIUSUm-EUTMqqg.png" /><figcaption>Remix usage in ChatGPT</figcaption></figure><p>And apparently they started before significant changes in Next.js</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*n3gb-igtHO-WHpA-NaAcbQ.png" /><figcaption><a href="https://x.com/btibor91/status/1828342132643332514">Tweet about ChatGPT switching to Remix</a> from August, 2024</figcaption></figure><p>In general, in the next stateofjs and stackoverflow surveys, we are likely to see significant reshuffling.</p><p><strong>Credits</strong></p><p>Code examples or their foundations are taken from next.js documentation, as well as from commits, PRs, and the next.js core;</p><p><strong>Postscript</strong></p><p>If you need a tool for generating documentation based on MD files — take a look at <a href="http://robindoc.com">robindoc.com</a>, if you work with next.js — you might find something useful in the solutions at <a href="http://nimpl.tech">nimpl.tech</a>.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=455cf606fc64" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Vercel VS Edge VS Next. What is Edge, why, how, and where]]></title>
            <link>https://alexdln.medium.com/vercel-vs-edge-vs-next-what-is-edge-why-how-and-where-2af3fc08c33b?source=rss-f9cb10da019e------2</link>
            <guid isPermaLink="false">https://medium.com/p/2af3fc08c33b</guid>
            <category><![CDATA[web-development]]></category>
            <category><![CDATA[nextjs]]></category>
            <category><![CDATA[react]]></category>
            <category><![CDATA[web]]></category>
            <category><![CDATA[front-end-development]]></category>
            <dc:creator><![CDATA[Alex Savelyev]]></dc:creator>
            <pubDate>Tue, 16 Jul 2024 14:22:21 GMT</pubDate>
            <atom:updated>2024-11-17T09:56:11.266Z</atom:updated>
            <content:encoded><![CDATA[<h3>Vercel Edge — what is it and how is it</h3><p>Edge runtime. One of the main functionality of Vercel — the company that developed and maintains next.js. However, its influence on the edge runtime has gone far beyond its frameworks and utilities. The edge runtime works in the recently acquired by Vercel Svelte, in nuxt, and in more than 30 other frontend frameworks. This article will focus on the edge runtime — what it is, how it is used in Vercel, what features it adds to next.js, expected changes and what solutions I made to expand these features.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*apbzG72t-UHdoDIX0HZ5Fw.png" /></figure><h3>Vercel Edge Network</h3><p>Simply put, the edge runtime is a content delivery network (<em>CDN/distributed infrastructure</em>), i.e., multiple points around the world. Thus, the user interacts not with a single server (<em>which may be located in the company’s office on the other side of the world</em>), but with the nearest network point.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*BsfeCjYM9fivJgiPiiVU5w.png" /><figcaption>Difference between direct server requesrs and through Edge Network</figcaption></figure><p>At the same time, these are not copies of the application, but separate functionalities that can work between the client and the server. In other words, these are mini-servers with their own features (<em>which will be described later</em>).</p><p>This system allows users to access not your far server right away, but a nearby point. Decisions on A/B tests are made at this point, authorization checks are performed, requests are cached, errors are returned, and much more. After this, if necessary, the request will go to the server for the required information. Otherwise, the user receives an error or, for example, a redirect in the shortest time.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*UXTxwsqHmOHqjZpLeuJPQA.png" /><figcaption>Processing requests through Edge Network</figcaption></figure><p>Of course, this concept itself is not Vercel’s achievement. CloudFlare, Google Cloud CDN, and many other solutions can do this as well. However, Vercel, with its influence on frameworks, has taken this to a new level, deploying not just an intermediate router at the CDN level but creating mini-applications capable of even rendering pages at the nearest point to the user. And most importantly, this can be done simply by adding familiar JS files to the project.</p><h3>Edge runtime in next.js</h3><p>In next.js, perhaps the main functionality of this environment is the middleware file. Any segment (<em>API or page</em>) can also be executed in the edge runtime. But before describing them, a little bit about the next.js server.</p><p>Next.js is a full-stack framework. That is, it contains both the client application and the server. When running next.js (next start) - it is the server that starts and is responsible for serving pages, working with the API, caching, rewrites, etc.</p><p>It all works in the following order:</p><ol><li>headers from next.config.js;</li><li>redirects from next.config.js;</li><li>Middleware;</li><li>beforeFiles rewrites from next.config.js;</li><li>Files and static segments (public/, _next/static/, pages/, app/, etc.);</li><li>afterFiles rewrites from next.config.js;</li><li>Dynamic segments (/blog/[slug]);</li><li>fallback rewrites from next.config.js.</li></ol><p>When it is determined that the current request reaches is the segment (<em>not, for example, a redirect</em>), its processing begins (<em>this is either the return of a statically assembled segment, reading from the cache, or its execution and return of the result</em>).</p><p>In Vercel, likely, this entire cycle can run in the edge runtime. However, points 3, 5, and 7 are particularly interesting here.</p><p>The middleware in its basic implementation looks like this:</p><pre>import { NextResponse, type NextRequest } from &#39;next/server&#39;;<br><br>export function middleware(request: NextRequest) {<br>  return NextResponse.redirect(new URL(&#39;/home&#39;, request.url));<br>}</pre><p>In it, for example, you can:</p><ul><li>Make requests (<em>e.g., to get data from third-party services</em>);</li><li>Perform a rewrite or redirect (<em>e.g., to run an A/B test or check authorization</em>);</li><li>Return some body (<em>e.g., to display a basic stub in certain situations</em>);</li><li>Read and/or modify headers and cookies (<em>e.g., save or read access information</em>).</li></ul><p>You can read more about the areas of application in the <a href="https://nextjs.org/docs/app/building-your-application/routing/middleware">next.js middleware documentation</a>.</p><p>The same can be done in segments (<em>i.e., API and pages</em>). To make a segment work in the edge runtime, you need to export from the segment file:</p><pre>export const runtime = &#39;edge&#39;;</pre><p>Thus, the segment will be executed in the edge runtime, not on the server itself.</p><p>However, it is important to make an important caveat here. Everything described above is not a full-fledged edge runtime by itself. In Edge Network, this will be distributed only when the service is deployed in Vercel.</p><p>Also, in addition to all these capabilities, the edge runtime has several limitations. For example, despite the fact that when running the application outside of Vercel, the edge runtime is part of the server — it will not be possible to interact with this server. And this is done because it was developed specifically for the Vercel Edge Network.</p><h3>Edge runtime concept in Vercel</h3><p>As mentioned, the edge runtime can be called mini-applications. They are mini because they run on node.js V8 (<em>which runs, for example, Google Chrome and Electron</em>). This is their key detail, on which not only the features of the previous section depend but also the restrictions.</p><p>Namely, in the edge runtime, you cannot:</p><ul><li>Perform actions with the file system;</li><li>Interact with the server environment;</li><li>Call require. Only ES modules can be used. This imposes additional restrictions on third-party solutions.</li></ul><p>The full list of supported APIs and restrictions can be found on the <a href="https://nextjs.org/docs/app/api-reference/edge">next.js documentation page</a>.</p><p>Thus, Vercel Edge Network can be responsible for, for example:</p><ul><li>Routing;</li><li>Page rendering;</li><li>Executing API routes;</li><li>Caching.</li></ul><p>The edge runtime acts as the first stage of segment processing and is most effective in situations where all processing can take place inside the edge container. For example, for redirects or returning cached data. The entire processing process in Vercel usually works in the following order:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*TdV0ajHDgT0N4ndnIXEjJg.png" /><figcaption>Request processing order in Vercel, source — <a href="https://vercel.com/docs/functions">vercel.com</a></figcaption></figure><p>After the build, Vercel sends the new edge runtime code (<em>which now compiles to machine code</em>) to the servers, and they immediately start working with the new code.</p><p>Vercel itself uses the edge runtime for all applications and all requests. That is, after connecting the domain, Vercel immediately configures that this domain is also available on these points around the world. The next time a user visits the page, the provider will ask the network where this domain is located, and in response, it will get available locations, choose the nearest point, and go to it.</p><p>These Edge points always have caching logic, and if there are rewrites, redirects, middleware, or segments in the edge runtime in the project — after the build, it will send all of this to the edge servers.</p><p>Then the edge runtime will process it: check rewrites and redirects -&gt; pass through middleware -&gt; check if it is cached -&gt; if the segment is in the edge runtime — execute it there, if not — send the request to the original server (<em>but Vercel does not document these orders and internals of the Edge runtime anywhere, but this is how I see it</em>).</p><p>In summary, it is beneficial to use the Edge runtime when all processing can be done within the Edge environment (The request will be <strong>Client -&gt; Edge</strong>). If you need to access the main server (<em>for example, a database connected within the project, or to read files for some reason</em>) — it is not advantageous. The request will still be <strong>Client -&gt; Edge -&gt; Server</strong>. And since you still need to access the server — it is better to do all the processing there — it has all the cache, the database is nearby, the whole system is nearby, and overall, it has more capabilities.</p><h3>Expected changes in the edge runtime</h3><p>Despite the fact that the edge runtime is one of the key features of Vercel as a hosting, the team is actively revising it. Not only its application but also its necessity as a whole. Recently, Vercel VP <a href="https://twitter.com/leeerob/status/1780705942734331983">Lee Robinson in his tweet</a> shared that Vercel [as a company] stopped using the edge runtime in all its services and returned to the nodejs runtime. The team also expects that the experimental partial pre-render (<em>PPR</em>) will be so effective that edge runtime generation will lose its value.</p><p>And it was PPR along with advanced caching that pushed the edge runtime into the background. Previously, the entire page was rendered either on the server or in the edge runtime. The edge runtime won precisely because of its closer location. Now, pages are mostly pre-generated. Then, upon request, individual dynamic parts are rendered and cached. The cache, in turn, is unique for each point in the edge runtime, whereas on the server it is the same for all users.</p><p>And, of course, the server has access to the environment, database, and file system. Therefore, if the page needs this data, the nodejs runtime wins significantly (<em>gathering everything in one environment is faster than making requests to the server from the edge environment each time</em>).</p><p>Vercel is likely to introduce new priorities in its pricing, restructuring them around partial pre-render. <em>Perhaps with these changes, tweets with bills of tens of thousands of dollars will become fewer (but this is not certain).</em></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*1IL_BX_HCDOYKKU6x3G1Yw.png" /><figcaption>Invoice for using Vercel for $100000, source — <a href="https://x.com/zemotion/status/1800959534057529394">tweet by Cara founder</a>.</figcaption></figure><p>In addition, the Next.js team recently shared a <a href="https://x.com/feedthejim/status/1807083933156147250">tweet about middleware revision</a>. It is very likely that, like the segments, it will be given an execution environment choice. Again, considering that outside Vercel middleware works as part of the server, this is a very logical decision. It is also possible that with these changes, a separate middleware for API routes will be added.</p><h3>Expanding the Edge runtime</h3><p>I am the author of several packages for next.js <a href="https://nimpl.tech/">nimpl.tech</a>. I have already mentioned getters with information about the current page in the article “<a href="https://medium.com/@vordgi/next-js-app-router-experience-of-use-path-to-the-future-or-wrong-turn-277dde4369a6">Next.js App Router. Experience of use. Path to the future or a wrong turn</a>”, translation library in “<a href="https://medium.com/@vordgi/how-i-revisited-i18n-47470ef71c91">More libraries to the god of libraries or how I rethought i18n [next.js v14]</a>”, caching packages in “<a href="https://medium.com/@vordgi/caching-in-next-js-gift-or-curse-095a35b9723a">Caching in next.js. Gift or curse</a>”. But in this family, there are also packages specifically built for the edge runtime — <a href="https://nimpl.tech/router">router</a> and <a href="https://nimpl.tech/middleware-chain">middleware-chain</a>.</p><h3>@nimpl/router</h3><p>As mentioned, the edge runtime works best if it can handle the entire request in a self-contained mini-application. In all other cases, this is an unnecessary step since the request will still go to the server but via a longer path.</p><p>One of these tasks is routing. Routing also includes rewrites, redirects, basePath, and i18n from next.config.js.</p><p>Their main problem is that they are set only once — in the configuration file — for the entire application, and also, i18n is full of bugs. Therefore, including in the App Router, there is no information about the i18n option, and the documentation recommends using middleware for this case. But such a separation means that redirects from the config and i18n routing from middleware are processed separately. This can cause double redirects (<em>first a redirect from the config will be performed, then a redirect from the middleware</em>) and various unexpected artifacts can appear.</p><p>To avoid this, all this functionality should be gathered in one place. And, as the documentation recommends for i18n, this place should be middleware.</p><pre>import { createMiddleware } from &#39;@nimpl/router&#39;;<br><br>export const middleware = createMiddleware({<br>    redirects: [<br>        {<br>            source: &#39;/old&#39;,<br>            destination: &#39;/&#39;,<br>            permanent: false,<br>        },<br>    ],<br>    rewrites: [<br>        {<br>            source: &#39;/home&#39;,<br>            destination: &#39;/&#39;,<br>            locale: false,<br>        },<br>    ],<br>    basePath: &#39;/doc&#39;,<br>    i18n: {<br>        defaultLocale: &#39;en&#39;,<br>        locales: [&#39;en&#39;, &#39;de&#39;],<br>    },<br>});</pre><p>Familiar Next.js redirects, rewrites, basePath, and i18n settings but at the edge runtime level. <a href="https://nimpl.tech/router">Documentation for the @nimpl/router package</a>.</p><h3>@nimpl/middleware-chain</h3><p>Working with ready-made solutions or creating my own, time and time again I encountered the problem of combining them in one middleware. That is when you need to connect two or more ready-made middleware to one project.</p><p>The problem is that middleware in next.js is not the same as in express or koa — it immediately returns the final result. Therefore, each package just creates the final middleware. For example, in next-intl, it looks like this:</p><pre>import createMiddleware from &#39;next-intl/middleware&#39;;<br><br>export default createMiddleware({<br>  locales: [&#39;en&#39;, &#39;de&#39;],<br>  defaultLocale: &#39;en&#39;,<br>});</pre><p>I am not the first to encounter this problem, and ready-made solutions can be found on npm. They all work through their own APIs — made in the style of express or in their own vision. They are useful, well-implemented, and convenient. But only in cases where you can update each used middleware.</p><p>However, there are many situations where you need to add already existing solutions. Usually, in the issues of these solutions, you can find “add support for adding package chain A”, “work with package chain B”. It is for such situations that <a href="https://nimpl.tech/middleware-chain">@nimpl/middleware-chain</a> is created.</p><p>This package allows you to create a chain of native next.js middleware without any modifications (<em>that is, you can add </em><strong><em>any</em></strong><em> ready-made</em> middleware <em>to the chain</em>).</p><pre>import { default as authMiddleware } from &quot;next-auth/middleware&quot;;<br>import createMiddleware from &quot;next-intl/middleware&quot;;<br>import { chain } from &quot;@nimpl/middleware-chain&quot;;<br><br>const intlMiddleware = createMiddleware({<br>    locales: [&quot;en&quot;, &quot;dk&quot;],<br>    defaultLocale: &quot;en&quot;,<br>});<br><br>export default chain([<br>    intlMiddleware,<br>    authMiddleware,<br>]);</pre><p>The chain processes each middleware sequentially. During processing, all modifications are collected until the chain is completed or until any element in the chain returns FinalNextResponse.</p><pre>export default chain([<br>    intlMiddleware,<br>    (req) =&gt; {<br>        if (req.summary.type === &quot;redirect&quot;) return FinalNextResponse.next();<br>    },<br>    authMiddleware,<br>]);</pre><p>This is not Koa or Express, this is a package for next.js, in its unique style and format of its API. <a href="https://nimpl.tech/middleware-chain">Documentation for the @nimpl/middleware-chain package</a>.</p><p><em>And to end, let me leave a few links here.</em></p><p><a href="https://vordgi.medium.com/">My Medium with other useful articles</a> | <a href="https://nimpl.tech/">nimpl.tech with package documentation</a> | <a href="https://www.notion.so/vordgi/github.com/vordgi/">github with star button</a> | <a href="https://x.com/vordgi">X with rare posts</a> | <a href="https://www.linkedin.com/in/vordgi/">in just to have it</a></p><p>The dot map used as the background for images at the beginning of the article is made by <a href="https://www.freepik.com/free-vector/earth-globe-world-map-dots-global-geography-dotted-pattern_13031912.htm#query=world">mocrovector from freepik</a>.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=2af3fc08c33b" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Measure twice and release once. A/B tests of static sites]]></title>
            <link>https://alexdln.medium.com/measure-twice-and-release-once-a-b-tests-of-static-sites-6fab896d28d1?source=rss-f9cb10da019e------2</link>
            <guid isPermaLink="false">https://medium.com/p/6fab896d28d1</guid>
            <category><![CDATA[web-development]]></category>
            <category><![CDATA[reactjs]]></category>
            <category><![CDATA[nextjs]]></category>
            <category><![CDATA[react]]></category>
            <category><![CDATA[front-end-development]]></category>
            <dc:creator><![CDATA[Alex Savelyev]]></dc:creator>
            <pubDate>Tue, 04 Jun 2024 11:18:29 GMT</pubDate>
            <atom:updated>2024-06-04T11:43:06.079Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*RtDHWhlSmuxP0pCb7Xxi1w.png" /></figure><p>A release starts with an idea. When that perfect idea comes in the brainstorming, the idea that will appeal to all users and attract new customers. The idea is presented to a team of managers, marketers and is unconditionally supported by everyone.</p><p>The technical task is elaborated and the task is given to the developers. They grumble, asking to make an unnecessary updates, set clearly inflated deadlines, but eventually do the task. The task is testing and going to the end users. At this point, the life cycle of the idea is complete. Now all that remains is to wait for a mass of fresh analytics and celebrate…</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*7SiMFdzKfBKHsRIUeUS6Rw.png" /></figure><p>Some losses are allowed in the first week — there is little data, users are getting used to the updates, other improvements are being rolled out, a high influence of outliers. However, by the second week, it becomes evident that the idea not only did not attract new clients but also made some users use this product less.</p><p>The idea, which has gone through dozens of discussions and received hundreds of enthusiastic comments, has failed.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Yc3G0-HerOZn7NcPVfktBg.png" /></figure><h3>The Hypothesis Failed</h3><p>The introduction turned out to be voluminous. But I wanted to start this article with the long journey of the hypothesis. Because it broke at the very beginning — it was supported only by people similar to its author. But these people are not the most suitable target audience, and maybe even their rare exceptions.</p><p>That’s why when changing existing functionality, they do not rely on the thoughts of the author and the team. To make the right choice, they conduct research, analyze existing product and market analytics, compare with competitors. But often behind all these methods goes the only reliable way to test the hypothesis on the business audience. This (<em>attention!</em>) — is to test it specifically on the business audience.</p><p>But not on all of it. This method is called A/B testing. And all the further narration will be devoted to it.</p><h3>A/B Testing</h3><p>As we found out above — A/B test is testing a hypothesis on the very audience of the business. This test is due to the comparison of one functionality (option A) and another (option B).</p><p><em>Sometimes A/B tests are conducted alternately — that is, they first measure option A, and then, the next week they measure option B. This variant will not be described in the article because it does not represent anything interesting in technical terms (and here will be nothing about data collection and analysis).</em></p><p>A/B testing can take place for checking changes — in which case option A remains the current functionality, as well as for checking several implementations of a new idea — in this case, both options contain new functionality and they are compared relative to each other.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*WbMEf-waU_lOT24u6UWXXg.png" /></figure><p>Despite the name, there can be any number of options. The main thing is that the audience allows it. That is, it should be possible to collect enough data for each option, excluding outliers and interference.</p><p>So, suppose a decision is made to make a critical change to the website or application. At this moment, the seriousness of this change is assessed and a decision is made to implement it through an A/B test. At the same time, depending on the risks, they decide how to distribute the traffic.</p><p>Often the test starts with showing the new option only to 10% of users. Then, if the changes did not lead to a sharp deterioration in metrics on these 10% — it is extended to half of the users, so that the comparison would be full-fledged. Based on the results of this test, make a decision — to leave the new option or return the previous one.</p><p>At the same time, of course, according to the results of testing, it is possible to return the idea for revision and then launch the updated test. This can be repeated dozens of times until the necessary change leads to business metrics growth.</p><h3>A/B Test Rules</h3><ul><li>Variants should contain only those changes that are being tested. The basic rule, but, for example, along with adding a new block to the page, you may want to change its colors. As a result, all changes will affect the results and it will be impossible to understand how the addition of the block specifically influenced.</li><li>A rule related to the first one — all test options should fall out to the user at the same speed, delay, and, of course, bugs.</li><li>Another derivative rule — avoid overlapping tests. If 2 or more tests are conducted on one page (with same conditions), they all distort the results of each other and it is almost impossible to identify these distortions.</li><li>The user should not understand that he is participating in the test. Knowing this, the user may behave differently, for example, leave the service or reload the page to exit the test.</li><li>In interface tests, the user should spin the roulette wheel only once. In the future, he should see the option that fell out to him. Here is primarily a question of user experience — if he cannot see the same content when re-entering — he will have an unpleasant experience from the service.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*0waAySD5GTKDULoL3ujqJw.png" /><figcaption>Testing with a “small difference”. Source: <a href="https://www.pinterest.com/pin/this-black-cat-looks-like-toothless--5911043253756823/">Pinterest</a></figcaption></figure><h3>A/B Test Scheme</h3><p>Now, having sorted out what, why, and how is conducted, we can finally move on to the most interesting — the technical part.</p><p>And it is worth starting with the basic scheme of the application’s work:</p><p>Client — server — client</p><p>A very simple communication scheme. The client addressed to the necessary address, the server processed this request and response to the client.</p><p>With the advent of A/B tests, this scheme begins to work a little differently. Now, doing identical requests at the same time and under the same conditions, different answers are expected — those very option A or option B.</p><p>In practice, this is usually performed by a layer — either at the CDN level, a regular middleware on the server, or other intermediate tools, such as nginx (<a href="https://nginx.org/ru/docs/http/ngx_http_split_clients_module.html">module for conducting A/B tests in nginx</a>). Further, for the simplicity of the story, just middleware will be used.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*q14mikQOUs4d4c-yUwWvUQ.png" /></figure><p>In fact, A/B tests can be conducted entirely on the client side. This is how Google Optimizer worked (but in September 2023 it <a href="https://support.google.com/analytics/answer/12979939?hl=ru&amp;visit_id=638503139096943089-892581644&amp;rd=2">was deactivated</a>). The main problem with this approach was that the user who got option b was redirected to another page. This, in turn, made option B less comfortable for the user and gave out the testing.</p><p>This approach can be schematically described like this:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*-bhnkSKYkkEg3-lxGvVrKw.png" /></figure><h3>Implementation of A/B tests</h3><p>Below will be described the solution on next.js, but it can be repeated on any other technology that can change cookies and make rewrites (<em>or return a specific page</em>).</p><p>In next.js, this is done by middleware, which runs in the so-called edge-runtime, i.e., at the CDN level. In fact, outside of Vercel (<em>a platform for deploying applications, owning next.js</em>), it’s just a part of the server that works before processing routes.</p><p>The first and simplest method of testing is to show one of the options without any conditions:</p><pre>import { NextResponse, NextRequest } from &#39;next/server&#39;<br><br>export function middleware(request: NextRequest) {<br>    if (request.nextUrl.pathname === &#39;/home&#39;) {<br>        if (rollVariant() === 1) {<br>            return NextResponse.rewrite(new URL(&#39;/home-animated&#39;, request.url));<br>        } else {<br>            return NextResponse.rewrite(new URL(&#39;/home&#39;, request.url));<br>        }<br>    }<br>}</pre><p>The user has entered the /home page, in the middleware a random option is selected. If the user gets option B — the home-animated page is returned, otherwise the standard home.</p><p>It is more convenient to make the options of the interface test separate pages — a new option — a new page.</p><pre>root<br>--app<br>----about<br>------page.tsx<br>----home<br>------page.tsx<br>----home-animated<br>------page.tsx</pre><p>How to choose which option to show to the user? Just roll the dice! If less than half — variant a, otherwise — variant b.</p><pre>const rollVariant = () =&gt; Math.random() &lt; 0.5 ? 1 : 0;</pre><p>Now, depending on the rolled value, the user will receive from the server either a standard page or home-animated. For the same time and invisibly for the user.</p><p>However, with each entry, the user will get a random option. To prevent this from happening, you can write to the database that the client has become a participant in the A/B test. In the case of anonymous tests, test information can be saved in cookies and read from them in the future.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*KdEHGlpLnSc2W3neUiU_cw.png" /></figure><p>So, if the client already has a cookie recorded — you can skip the steps with the request check and option selection, and immediately issue the necessary page.</p><pre>import { NextResponse, NextRequest } from &#39;next/server&#39;<br><br>export function middleware(request: NextRequest) {<br>    if (request.nextUrl.pathname === &#39;/home&#39;) {<br>        const prevVariant = request.cookies.get(&#39;ab_variant&#39;);<br>        const variant = prevVariant ?? rollVariant();<br>        let next: NextResponse;<br>        if (variant === 1) {<br>            next = NextResponse.rewrite(new URL(&#39;/home-animated&#39;, request.url));<br>        } else {<br>            next = NextResponse.rewrite(new URL(&#39;/home&#39;, request.url));<br>        }<br>        next.cookies.set(&#39;ab_variant&#39;, variant.toString());<br>        return next;<br>    }<br>}</pre><p>Of course, these data need to be analyzed. Here are two options — send data from the server, in parallel with issuing the result to the user, or already on the client, having previously transferred the test results from the server. For the latter, you can use the previously created cookies.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*i4NOEB1332jTjXqdDfBRkg.png" /></figure><p>Further, it may be necessary to launch A/B tests only on a specific group. This can be a certain share of users, users of specific browsers, users from specific companies, or anything else.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*IiwRW1-t_UYKbDPQtZMdMg.png" /></figure><p>That is, it is necessary to check the user for a match and depending on the result, either include him in the test or not:</p><pre>import { NextResponse, NextRequest } from &#39;next/server&#39;<br><br>export function middleware(request: NextRequest) {<br>    if (request.nextUrl.pathname === &#39;/home&#39; &amp;&amp; request.nextUrl.searchParams.has(&#39;utm_campaign&#39;)) {<br>        // ...<br>    }<br>}</pre><p>Also, it may be necessary for only new users to participate. But, formally this is the same task as described above. This is a group of users who were not on the site before. In the case of anonymous users, it can be determined, for example, by the absence of cookies of the test, acceptance of policies, or analytics.</p><p>Of course, one test will not be enough and it will be necessary to run dozens, if not hundreds, of tests in parallel. The same logic will be used for this, but it will now be checked according to an array of instructions of the launched tests until the first suitable one.</p><p>Of course, every company will have different conditions, different requirements, and different orders. The basic example described above is a possible implementation, from which everyone can decide exactly what is needed and how.</p><p>Nevertheless, it was decided to try to implement a universal package for conducting A/B tests in next.js — <a href="https://nimpl.tech/ab-tests">@nimpl/ab-tests</a>.</p><h3>@nimpl/ab-tests</h3><p>The first thing to note is that the package meets all of the above, including all the rules. At the same time, it has a number of pleasant possibilities, executed in a familiar API for next.js developers.</p><p>The operation of the package can be described as follows:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*8gD-Zzug_qv97P_tSz_oZg.png" /></figure><p>The main advantage of the package is the principle of finding a suitable test. Each test may include the keys has and missing. Those familiar with next.js know these keys from working with rewrites and redirects. For example, a test can be described as follows:</p><pre>{<br>  id: &#39;some-id&#39;,<br>  source: &#39;/en-(?&lt;country&gt;de|fr|it)&#39;,<br>  has: [<br>    {<br>      type: &#39;query&#39;,<br>      key: &#39;ref&#39;,<br>      value: &#39;utm_(?&lt;ref&gt;moogle|daybook)&#39;,<br>    }<br>  ],<br>  variants: [<br>    {<br>      weight: 0.5,<br>      destination: &#39;/en-:country/:ref&#39;<br>    },<br>    {<br>      weight: 0.5,<br>      destination: &#39;/en-:country/:ref/new&#39;<br>    }<br>  ],<br>}</pre><p>This test will be performed for all users who come to the page with English locales and the label utm_*. Then the user will see either the base page for this company or a new one.</p><p>Each test also contains other keys, such as:</p><p>id - the identifier of the test, which will be written in the cookie;</p><p>source - another familiar key from next.js - the path on which the test is conducted;</p><p>variants - a list of options, which can be any number.</p><p>Each variant describes a weight — a weight and a destination (again, a familiar key from next.js). The main rule is that the total weight equals one.</p><h3>Additional part</h3><p>The development of the package did not end there. During the work on the package, it was decided to test its operation in several projects. However, adding a simple middleware turned out to be a real adventure. The problem is that the projects already had middleware — one with next-intl, one with next-auth.</p><p>Surprisingly, none of the projects had previously had the task of supporting two external middleware (only together with the internal ones). As a result of the search, it was not possible to find any solutions. All existing solutions work due to their own APIs — made in the style of express.js or even in their own vision. They are useful, well implemented, and convenient. But only in those cases when you can update every used middleware for them.</p><p>The situation here is quite different. It is necessary for each middleware to work as an original middleware from next.js. In general, another new solution was needed. I took it upon myself.</p><p>So @nimpl/middleware-chain appeared:</p><pre>import { default as authMiddleware } from &quot;next-auth/middleware&quot;;<br>import createMiddleware from &quot;next-intl/middleware&quot;;<br>import { chain } from &quot;@nimpl/middleware-chain&quot;;<br><br>const intlMiddleware = createMiddleware({<br>    locales: [&quot;en&quot;, &quot;dk&quot;],<br>    defaultLocale: &quot;en&quot;,<br>});<br>export default chain([<br>    intlMiddleware,<br>    authMiddleware,<br>]);</pre><p><em>A small and neat insert</em>.</p><p>You can check out these and other packages for next.js at <a href="http://nimpl.tech/">nimpl.tech</a>. Perhaps you’ll find some of the solutions useful (<em>such as the getter </em><a href="https://nimpl.tech/getters/current-getters/get-pathname"><em>getPathname</em></a><em> for server components or </em><a href="https://nimpl.tech/classnames-minifier"><em>class minifier</em></a>). Also, I have made publicly available a utility I use for editing groups of JSON files — <a href="https://www.npmjs.com/package/@nimpl/inio">@nimpl/inio</a>.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=6fab896d28d1" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[React Conf 2024]]></title>
            <link>https://alexdln.medium.com/react-conf-2024-76aabd9da1e1?source=rss-f9cb10da019e------2</link>
            <guid isPermaLink="false">https://medium.com/p/76aabd9da1e1</guid>
            <category><![CDATA[reactjs]]></category>
            <category><![CDATA[react]]></category>
            <category><![CDATA[website]]></category>
            <category><![CDATA[frontend]]></category>
            <category><![CDATA[web-development]]></category>
            <dc:creator><![CDATA[Alex Savelyev]]></dc:creator>
            <pubDate>Thu, 16 May 2024 00:07:32 GMT</pubDate>
            <atom:updated>2024-05-17T19:30:18.712Z</atom:updated>
            <content:encoded><![CDATA[<h3>React Conf 2024. React v19 RC</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*td5Jx-S0q6NsqL3s" /><figcaption>“React 19 RC”. Source: <a href="https://conf.react.dev/">React Conf 2024</a></figcaption></figure><p>The first day of React.js Conf just concluded. This much-anticipated conference took place almost 3 years after the previous one. The React updates were just as eagerly awaited. The conference began with these updates and this article will be dedicated to them. And yes, as you saw from the preview — version 19 has moved into the release candidate status. The full release is promised within two weeks.</p><p>Overall, as a next.js developer, most of it was familiar to me. Dozens of articles on the hub have already talked about almost every part of this update, and I partially touched on the updates introduced in next.js.</p><p>It can be said that the main directions of this update were achieving “High UX at high DX”. Maximum performance with maximally simple code. At the same time, there was almost no mention of server components in part of the updates, only indirectly. And so, let’s move on to the conference itself.</p><p>As usual in such conferences, everything starts with a description of growth. React downloads reached One Billion per year. The growth of the tool is inevitably linked to the growth of the community. Therefore, <a href="https://survey.stackoverflow.co/2023/">Stackoverflow statistics</a> were also shown — 40% of developers use react in web development, 36% are learning it.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*AX4tluSxn42BaeZB" /><figcaption>One billion React.js downloads per year. Source: <a href="https://conf.react.dev/">React Conf 2024</a></figcaption></figure><p>Also of interest, React functionality has become possible to a greater extent only within frameworks, so now React.js has started recommending specific ones. The slide showed remix, redwoodjs, next.js, and expo. Interestingly, there is no react router in this list.</p><p>Yes! React Router can now be added to this list. The first conference report was about him from Ryan Florence. Now with react router, you can not only do SPA but also SSR and SSG. This is now possible in conjunction with Vite. Hooks are available for working with data and server components.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*ijmMPzTpCAHJasf6" /><figcaption>Example with React Router v7. Source: <a href="https://conf.react.dev/">React Conf 2024</a></figcaption></figure><p>But for now, let’s return to the changes in React.js. The problem of coordinating elements and expanding the application was described next. JSX solved the problem of coordinating elements in UI development. Then they added Suspense, which solved the problem of coordinating elements when loading elements (<em>what to do during loading and what to show to the user at this time</em>).</p><p>In React 19, the following were also added:</p><ul><li>Server components. Among other things, such components can solve the problem of loading data and their further transfer to components (<em>using familiar props</em>);</li><li>Embedding metadata. Metatags can be embedded anywhere. React itself will add them to the right place and leave only one copy.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*wCvV-iojzN7XUM8M" /><figcaption>Embedding metadata in React 19. Source: <a href="https://conf.react.dev/">React Conf 2024</a></figcaption></figure><ul><li>Preloading methods. In addition to manually adding tags, methods for preloading links were also added — <a href="https://react.dev/reference/react-dom/preload">preinit</a>, <a href="https://react.dev/reference/react-dom/preload">preload</a>, <a href="https://react.dev/reference/react-dom/prefetchDNS">prefetchDNS</a> and <a href="https://react.dev/reference/react-dom/preconnect">preconnect</a>. The documentation also describes the <a href="https://react.dev/reference/react-dom/preloadModule">preloadModule</a> and <a href="https://react.dev/reference/react-dom/preinitModule">preinitModule</a> methods, <em>but for some reason, they were not shown at the conference</em>.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*8mHin6U9xWEmfptE" /><figcaption>Preloading Methods in React 19. Source: <a href="https://conf.react.dev/">React Conf 2024</a></figcaption></figure><ul><li>Embedding styles. Similar to meta tags, work will be done for styles. However, here an option of prioritization is added — depending on it, it will be determined which styles are more important (<em>and accordingly will be lower in the DOM</em>).</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*e2XAWDs--RR2dibm" /><figcaption>Embedding Styles in React 19. Source: <a href="https://conf.react.dev/">React Conf 2024</a></figcaption></figure><p>The style loading of the component is now tracked by suspense. That is, you can display the loader not only while the component is rendering, but also while its specific styles are being prepared.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*JFZe-QSCvbRL13L_" /><figcaption>“Suspense for CSS”. Source: <a href="https://conf.react.dev/">React Conf 2024</a></figcaption></figure><p>With the advent of server components, React took on more responsibility in terms of server rendering, as a result, hydration takes on even more logic and potential problems. The React.js team improved hydration errors.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*va0PdCRg1YY07wYI" /><figcaption>Comparison of hydration errors in React18 and React 19. Source: <a href="https://conf.react.dev/">React Conf 2024</a></figcaption></figure><p>In addition to significant changes in working with the construction of a real tree, the logic of interaction with forms has been updated in React.js. First of all, the <a href="https://react.dev/reference/react-dom/components/form">form</a> component was included in react-dom, which means significant changes over the element. And first of all, this change concerns the change of the “<a href="https://react.dev/reference/react-dom/components/form#handle-form-submission-on-the-client">action</a>” attribute — as an alternative to submitting a form through onSubmit or a native attribute.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*c1cF42ezFHIr5MFk" /><figcaption>Pros and cons of the action attribute and onSubmit in React. Source: <a href="https://conf.react.dev/">React Conf 2024</a></figcaption></figure><p>Adding action itself looks just like onSubmit, but instead of an event, it immediately takes FormData.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*vCwB9YJuChY0xKPI" /><figcaption>Using action in React 19. Source: <a href="https://conf.react.dev/">React Conf 2024</a></figcaption></figure><p>Also, for form fields and buttons, the formAction prop has been added, which works in an identical manner.</p><p>Perhaps the basic advantage of using action instead of onSubmit is that with client actions, if the user calls form submission immediately (<em>even before loading the form logic</em>) — its submission will be postponed and will be performed as soon as the logic is ready. In server actions, the submission will occur immediately because it does not require client js.</p><p>But, in addition to the basic difference, there are significant changes in interaction with form submission — these are new hooks. <a href="https://react.dev/reference/react/useOptimistic">useOptimistic</a>, <a href="https://react.dev/reference/react-dom/hooks/useFormStatus">useFormStatus</a> and <a href="https://react.dev/reference/react/useActionState">useActionState</a>.</p><p>Sam Selikoff shared examples of working with them in his presentation “React unpacked: A Roadmap to React 19”. For example, this is what replacing onSubmit with action + useActionState looks like:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*IfVIkYuLfQEz7DRt" /><figcaption>Using useActionState in React 19. Source: <a href="https://conf.react.dev/">React Conf 2024</a></figcaption></figure><p>Then you can add optimistic render:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*5VI6x423fnBqV2lk" /><figcaption>Using useOptimistic in React 19. Source: <a href="https://conf.react.dev/">React Conf 2024</a></figcaption></figure><p>And again, let’s return from the report to the key changes. A relatively small change was shown next, but very, very valuable. In React.js 19, you can pass ref to a functional component as props. Right away. Without forwardRef.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*5jZ_2HStRT0XdY-A" /><figcaption>“Never use forwardRef again!”. Source: <a href="https://conf.react.dev/">React Conf 2024</a></figcaption></figure><p>Also, when passing ref to components, you can return a callback on unmount.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*F6j-_qYxQcgSH7mQ" /><figcaption>Callback function on unmount of a component in React 19. Source: <a href="https://conf.react.dev/">React Conf 2024</a></figcaption></figure><p>The final key change was the <a href="https://react.dev/learn/react-compiler">React Compiler</a>. An advanced loader with memoization out of the box. Together with him, React will automatically set up memoization in the application. Lauren Tan elaborated on this in her presentation “React Compiler Case Studies”.</p><p>So, to understand how to set up memoization, React analyzes the relationships from the place that triggers the rerender to the endpoints:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*jrMQ6lrDM0rlyK-p" /><figcaption>Relations for React compiler. Source: <a href="https://conf.react.dev/">React Conf 2024</a></figcaption></figure><p>Based on these relationships, the compiler can imagine a full graph of dependencies:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*p4ibj1t-Uud5i4ix" /><figcaption>Graph of relations for React Compiler. Source: <a href="https://conf.react.dev/">React Conf 2024</a></figcaption></figure><p>And then, depending on these connections, set up memoization with the necessary dependencies. In this case, since songs do not change — filteredSongs should remain the same (<em>they will be memoized with a dependency on songs</em>), and if the song is changed by setSong, NowPlaying should be rerendered (<em>it will be memoized with a dependency on song</em>).</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*XUKRvBaXPMssx7kX" /><figcaption>Dependencies in rerenders for React Compiler. Source: <a href="https://conf.react.dev/">React Conf 2024</a></figcaption></figure><p>“Maximum performance with maximally simple code”.</p><p><em>A great solution, however, it will be interesting to see how memoizations will be set up in practice — where developers should write them, and where it is worth not complicating and leaving this logic to the compiler.</em></p><p>You can <a href="https://react.dev/learn/react-compiler#usage-with-babel">install the compiler</a> right now on all major frameworks and build systems that support babel. It is already being used in Instagram, Facebook, and Bluesky (<em>the company where Dan Abramov now works</em>).</p><p>Also, to increase reliability and quality of compilation, you can install an eslint plugin, which will indicate all problems with code optimization. In general, the plugin can be used independently of the compiler.</p><pre>npm install eslint-plugin-react-compiler</pre><p>You can also use a command-line utility that will check the application for possible optimization by the compiler</p><pre>npx react-compiler-healthcheck</pre><p>Another innovation was shared by Lydia Hallie — the <a href="https://react.dev/reference/react/use">use</a> function. Yes, it’s not a mistake — it’s not a hook.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*AOIHmjQ_1DMFqNFI" /><figcaption>use in React 19. Source: <a href="https://conf.react.dev/">React Conf 2024</a></figcaption></figure><p>The key difference between use and hooks is that use can be used within conditions</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*aEFNJs_g5z2_k2kk" /><figcaption>Using use in React 19. Source: <a href="https://conf.react.dev/">React Conf 2024</a></figcaption></figure><p>Use itself can take either a promise or a context. It’s hard to imagine a situation where you can’t be sure what argument will be passed to use — a promise or context and why not just make two independent functions.</p><p>In conclusion, I will note the amazing reports, presentations, examples, and performances in general. The React.js team was really able to show the possibilities of all improvements (<em>next.js team forgive me but they didn’t even come close</em>). Also, from a pleasant difference, I note that the React.js team refused to include in the core the rewrite of the fetch API and <a href="https://github.com/facebook/react/pull/28896">rolled back</a> already finished changes.</p><p>The general list of changes looks like this:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*H4iFptOJ9gi7x-js" /><figcaption>Updates in React 19. Source: <a href="https://conf.react.dev/">React Conf 2024</a></figcaption></figure><p><em>UPD: Seems like I can leave some advertising here.</em></p><p>If you’re using next.js — you might want to check out the solutions from <a href="http://nimpl.tech/">nimpl.tech</a>, you might find some of them useful (<em>like for example the getter </em><a href="https://nimpl.tech/getters/current-getters/get-pathname"><em>getPathname</em></a><em> for server components or the package for configuration </em><a href="https://nimpl.tech/config"><em>config</em></a><em> set up for all next.js environments</em>)</p><p><em>UPD2: There are still entries here, so I’ll leave another update.</em></p><p>I just submitted a <a href="https://github.com/vercel/next.js/pull/65897">PR</a> for the addition of getPageContext functionality in next.js (<em>for non-next.js readers — the huge pain of working with contexts in server components [because there simply isn’t one and there are no alternatives]</em>). Leave a reaction if you’re familiar with this pain.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=76aabd9da1e1" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Caching in next.js. Gift or Curse]]></title>
            <link>https://alexdln.medium.com/caching-in-next-js-gift-or-curse-095a35b9723a?source=rss-f9cb10da019e------2</link>
            <guid isPermaLink="false">https://medium.com/p/095a35b9723a</guid>
            <category><![CDATA[nextjs]]></category>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[front-end-development]]></category>
            <category><![CDATA[react]]></category>
            <category><![CDATA[web-development]]></category>
            <dc:creator><![CDATA[Alex Savelyev]]></dc:creator>
            <pubDate>Tue, 19 Mar 2024 14:22:13 GMT</pubDate>
            <atom:updated>2024-03-19T14:22:13.850Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Zw_akPv6cJatJ8xZsMOFGg.jpeg" /></figure><p>In version 13, the next.js team introduced a new approach to application design — the so-called App Router. In version 14, it was made stable and primary for new applications.</p><p>The App Router significantly expands the functionality of next.js — partial pre-rendering, templates, parallel and interceptable routes, server components, and much more. However, despite all these improvements, not everyone has decided to switch to the App Router. And there are reasons for that.</p><p>I briefly discussed the advantages and problems of the new router in the article “Next.js App Router. Experience of use. The path to the future or the wrong turn”. Further, the conversation will not be about new abstractions or their features. In fact, the key and most controversial change is caching. This article will explain what, why, and how the most popular frontend framework, Next.js, caches.</p><h3>What does next.js cache?</h3><p>On the next.js website, you can find excellent <a href="https://nextjs.org/docs/app/building-your-application/caching">documentation on the caching process</a>. First, a brief overview of the main points from the article.</p><p>Any request in next.js triggered through fetch will be memoized and cached. The same will happen with pages and the cache function. How this works under the hood will be discussed in the following sections. The general page building process works as follows:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*EG1gWo6ITmZIPk1ep-9SHw.jpeg" /><figcaption>Caching process in next.js. Source: <a href="https://nextjs.org/docs/app/building-your-application/caching">next.js docs</a></figcaption></figure><p>That is: the user goes to the page, a request for a route is sent to the server, the server starts rendering the route, sending the necessary requests along the way. Then all this is executed and cached.</p><p>In addition to the cache in the scheme, there is also memoization. It is needed for recurring requests — so that they are not sent several times, but are subscribed to the first one.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*c2RxuSEliqU85XQDEfV4ow.jpeg" /><figcaption>Memoization in next.js. Source: <a href="https://nextjs.org/docs/app/building-your-application/caching">next.js docs</a></figcaption></figure><p>Caching on the server is done using the so-called Data Cache. You can remove data from it by calling the revalidatePath and revalidateTag functions. The first one will update the cache for the page, the second one for the tag specified in the requests.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*SlEafz0IDkF9wM2hZwRD4g.jpeg" /><figcaption>Cache revalidation in next.js. Source: <a href="https://nextjs.org/docs/app/building-your-application/caching">next.js docs</a></figcaption></figure><p>Data is also cached on the client side — inside the client router.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*M46xIEsAOk9f7gad9RMgrA.jpeg" /><figcaption>Client caching in next.js. Source: <a href="https://nextjs.org/docs/app/building-your-application/caching">next.js docs</a></figcaption></figure><p>Not mentioned in the article — next.js also caches rewrites and redirects. That is, if the user was once redirected from the / page to the /login on the server - now he will continue to be redirected there. This will be cached in the client router until the client cache is cleared.</p><p>You can clear the cache on the client using router.refresh or by calling revalidatePath and revalidateTag in server actions.</p><pre>&#39;use server&#39;<br><br>import { revalidateTag } from &#39;next/cache&#39;<br><br>export default async function submit() {<br>  await addPost()<br>  revalidateTag(&#39;posts&#39;)<br>}</pre><h3>Why is caching needed in next.js?</h3><p>The fetch from next.js is a wrapper over the native node.js fetch. The wrapper is configured to connect with the so-called Data Cache. This is done so that each request can be processed as described in the schemes above. The next.js team is most often criticized by the community for this replacement of the native API.</p><p>Later in next.js, the ability to disable caching of a request was added with the cache: &quot;no-store&quot; option. But even with this option, it will continue to be memoized. As a result, one of the key APIs for development has ceased to be controlled by the developer.</p><p>Nevertheless, there were reasons for this step. And it is unlikely that the initial reason was optimization. For optimization, it would been enough to create a new function for requests — a separate API, of which there are hundreds in next.js.</p><p>I followed a similar path when developing the <a href="https://github.com/vordgi/next-translation">next-translation</a> package (<em>as I wrote in a </em><a href="https://vordgi.medium.com/how-i-revisited-i18n-47470ef71c91"><em>previous article</em></a>). Then an interesting problem arose — too many requests were going to the server (<em>not triggered through fetch</em>). Looking into the reasons and reading the next.js source code, it became clear that the application is now being built in several independent threads. <em>Strange, why they didn’t talk about this in the latest releases.</em> Each thread lives as an independent process, as a result, it was not possible to make normal caching for the entire application inside the package.</p><p>The same problem arose before the next.js team — each integration, each package, each user now began to send several times more requests, and the previously configured caching systems stopped working correctly. And as a solution — the remodeling of fetch and hiding this feature under the hood.</p><h3>How does Data Cache work?</h3><p>The saving of loaded or generated data occurs in the so-called cacheHandler. Out of the box, next.js has 2 options for cacheHandlers — FileSystem and Fetch. This cacheHandler will be used both for caching requests and for caching pages.</p><p>FileSystem is used by default, saves data to the file system, additionally memoizing in-memory. FileSystem copes well with its task, but it has one drawback — it works as part of the application. From this it follows that if the application is created in several replicas — each of them will have an independent cacheHandler.</p><p>This problem is especially felt when the application works in ISR mode. You need to get into each replica and revalidate the cache in each of them. At the same time, check that they will load the same data. Also, if 2 replicas work with one folder — conflicts can arise in the file system during recording.</p><p>Probably for this reason, you can find Fetch variant in the framework code. It saves the cache to a remote server. However, this cacheHandler is only used when publishing the application in Vercel, as it saves data on Vercel servers.</p><p>As a result, the out-of-the-box solution does not cover all needs — FileSystem is not suitable if there are several replicas, and Fetch if the application is not deployed in Vercel. An important feature is that next.js allows you to write your own cacheHandler. To do this, you need to pass to the application configuration the path to the file with the class (CacheHandler), in which the get, set and revalidateTag methods will be described:</p><pre>// cache-handler.js<br>module.exports = class CacheHandler {<br>  constructor(options) {<br>    this.options = options<br>  }<br><br>  async get(key) {<br>    // ...<br>  }<br><br>  async set(key, data, ctx) {<br>    // ...<br>  }<br><br>  async revalidateTag(tag) {<br>    // ...<br>  }<br>}</pre><p>And connect it in the application configuration:</p><pre>module.exports = {<br>  cacheHandler: require.resolve(&#39;./cache-handler.js&#39;),<br>  cacheMaxMemorySize: 0, // disable default in-memory caching<br>}</pre><p>One of these cacheHandlers is <a href="https://github.com/vercel/next.js/tree/canary/examples/cache-handler-redis">cache-handler-redis</a>, which the next.js team referred to in the last release.</p><h3>Key points</h3><p>Next.js caches a large part of the processes.</p><p>Caching occurs in several stages — caching transitions and pages in the client router, memoization of the request, caching on the server of requests and pages.</p><p>Quite often, applications are launched in several replicas. Replicas need a common cache, especially this is acute when the application works in ISR mode.</p><p>The application itself is assembled in several threads that do not have access to each other.</p><p>The cacheHandler is responsible for caching. Next.js has two out-of-the-box options — working with the file system and working with a remote server, but the latter is only available within Vercel.</p><p>You can write your own cacheHandler.</p><h3>Caching refinement</h3><p>Let’s go back to the next-translation package. To solve the problem of unnecessary requests, I came up with an interesting way out — to raise an additional server and process requests going through it — as a result, all requests go from one place, which means caching can be configured in it. This is a principle similar to FetchCacheHandler and the approach in Vercel in general — when during the build the data is cached on the vercel server, and since the server is nearby, this works quickly.</p><p>However, caching is too much responsibility for a translation library. The next task was to overhaul the caching logic to combine the next.js API, libraries, and solve common problems. As a result, another library was created — <a href="https://github.com/vordgi/next-impl-cache">next-impl-cache-adapter</a>.</p><h3>Cache management</h3><p>As already mentioned, for a common cache between instances (replicas, copies) — the cache must be separate from each instance of the application. next-impl-cache-adapter solves this by creating a separate service.</p><p>This service is a server in which the desired cacheHandler works. Each application instance will process requests through this server. <em>At the same time, the server does not need to be restarted with each build. Outdated data will be automatically deleted during the launch of a new version of the application.</em></p><p>Server code:</p><pre>// @ts-check<br>const createServer = require(&#39;next-impl-cache-adapter/src/create-server&#39;);<br>const CacheHandler = require(&#39;next-impl-cache-in-memory&#39;);<br><br>const server = createServer(new CacheHandler({}));<br>server.listen(&#39;4000&#39;, () =&gt; {<br>    console.log(&#39;Server is running at &lt;http://localhost:4000&gt;&#39;);<br>});</pre><p>In this example, the server is passed <a href="https://github.com/vordgi/next-impl-cache/tree/main/packages/next-impl-cache-in-memory">next-impl-cache-in-memory</a> — this is a basic cacheHandler that saves data in-memory.</p><p>A special adapter for working with the cache is configured in the application itself:</p><pre>// cache-handler.js<br>// @ts-check<br>const AppAdapter = require(&#39;next-impl-cache-adapter&#39;);<br>const CacheHandler = require(&#39;next-impl-cache-in-memory&#39;);<br><br>class CustomCacheHandler extends AppAdapter {<br>    /** @param {any} options */<br>    constructor(options) {<br>        super({<br>            CacheHandler,<br>            buildId: process.env.BUILD_ID || &#39;base_id&#39;,<br>            cacheUrl: &#39;http://localhost:4000&#39;,<br>            cacheMode: &#39;remote&#39;,<br>            options,<br>        })<br>    }<br>}<br><br>module.exports = CustomCacheHandler;</pre><p>The created adapter is connected in the next.js configuration:</p><pre>// next.config.js<br><br>module.exports = {<br>  cacheHandler: require.resolve(&#39;./cache-handler.js&#39;),<br>  cacheMaxMemorySize: 0, // disable default in-memory caching<br>}</pre><p>The package supports three caching options: local, remote and isomorphic.</p><p><strong>local</strong></p><p>Standard solution. The cache is processed next to the application. It is convenient to use in development mode and on stages where the application is launched in one instance.</p><p><strong>remote</strong></p><p>The entire cache will be written and read on the created remote server. Convenient to use for applications launched in several replicas.</p><p><strong>isomorphic</strong></p><p>The cache operates next to the application, but also saves data to a remote server. Convenient to use during assembly, preparing the cache for the moment of launching application instances, but without spending resources on loading the cache from a remote server.</p><p>As a cacheHandler, it can be any cacheHandler supported by next.js. And vice versa, cacheHandlers from the package can be directly connected in next.js.</p><h3>Conclusions</h3><p>The App Router introduced a lot of very useful updates, but lost in convenience, predictability, and versatility. First of all, due to caching. After all, this is a task in which there is no and cannot be a universal solution. The ability to disable caching for a request and write your own cacheHandler solves most of the problems. However, memoization and caching in the client router remain out of control.</p><p>The next.js team itself is in no hurry to develop solutions for specific tasks. For this reason, since the release of the stable App Router, I continue to work on the implementation of packages that solve next.js problems. Along the way, telling about them in articles.</p><p><em>Let’s make the web not only faster, but also clearer.</em></p><h3>Links</h3><p><a href="https://github.com/vordgi/next-impl-cache">next-impl-cache</a> — solutions for setting up caching in next.js.</p><p><a href="https://github.com/vordgi/next-impl-getters">next-impl-getters</a> — implementation of server getters and contexts in React Server Components without switching to SSR.</p><p><a href="https://github.com/vordgi/next-impl-config">next-impl-config</a> — adding support for configuration for each possible next.js environment (build, server, client, and edge).</p><p><a href="https://github.com/vordgi/next-classnames-minifier">next-classnames-minifier</a> — compression of classes to characters (.a, .b, …, .a1).</p><p><a href="https://github.com/vordgi/next-translation">next-translation</a> — i18n library, developed with consideration of server components and maximum optimization.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=095a35b9723a" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[History of Vercel 2020-Present (7/7). Zeit is now Vercel]]></title>
            <link>https://medium.com/history-of-vercel/history-of-vercel-2020-present-7-7-zeit-is-now-vercel-c6fde0b931e6?source=rss-f9cb10da019e------2</link>
            <guid isPermaLink="false">https://medium.com/p/c6fde0b931e6</guid>
            <category><![CDATA[technology]]></category>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[react]]></category>
            <category><![CDATA[front-end-development]]></category>
            <category><![CDATA[startup]]></category>
            <dc:creator><![CDATA[Alex Savelyev]]></dc:creator>
            <pubDate>Wed, 28 Feb 2024 11:07:35 GMT</pubDate>
            <atom:updated>2024-08-15T21:04:59.739Z</atom:updated>
            <content:encoded><![CDATA[<p>In April 2020, tech company Zeit announced a major rebranding. This new turn allowed Guillermo Rauch to return to big business, but at the same time, it became the most controversial decision in the eyes of the community.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*E2XerxFqMgpWD0TPU70u0g.jpeg" /></figure><p>Nonetheless, this decision had objective reasons. Zeit could be labeled a failure in terms of investments — essentially, it was not perceived as a startup and that was the main problem. Some of the investors in the future company Vercel — CRV — said they were “delighted to resume business with Guillermo, after he returned to the path of entrepreneurship and founded Vercel”.</p><p>The decision, so critically perceived by the community, turned out to be extremely effective for attracting investors. Immediately after the rebranding, now the company Vercel, announced the collection of $21 million. Soon the company received another $40 million, then $102 million — then Vercel was valued at $1.1 billion, becoming Guillermo’s first unicorn.</p><p>In this, the final part of the series, we will talk about the extreme, the most important, and successful project of Guillermo — the company Vercel.</p><p><strong>History of Vercel</strong></p><ol><li><a href="https://medium.com/history-of-vercel/history-of-vercel-annotation-41a62b0cf53c">History of Vercel. Annotation</a>;</li><li><a href="https://medium.com/history-of-vercel/history-of-vercel-1990-2009-guillermo-rauch-childhood-and-first-steps-in-programming-1dbf038ddf9a">History of Vercel (1/7). 1990–2009. Guillermo Rauch. Childhood and first steps in programming</a>;</li><li><a href="https://medium.com/history-of-vercel/history-of-vercel-2009-2013-first-startup-by-guillermo-rauch-learnboost-7da50d365f0f">History of Vercel (2/7). LearnBoost. A leading tech company</a>;</li><li><a href="https://medium.com/history-of-vercel/the-history-of-vercel-2009-2013-learnboost-team-and-open-source-27c63d12549a">History of Vercel (3/7). 2009–2013. LearnBoost. Team that has become a leader in open source</a>;</li><li><a href="https://medium.com/history-of-vercel/history-of-vercel-cloudup-0cb0c0899bbd">History of Vercel (4/7). 2013. Cloudup. Drag. Drop. Stream</a>;</li><li><a href="https://medium.com/history-of-vercel/history-of-vercel-2013-2015-5-7-automattic-5cf2c1b6c98a">History of Vercel 2013–2015 (5/7). Automattic</a>;</li><li><a href="https://medium.com/history-of-vercel/history-of-vercel-2015-2020-6-7-zeit-and-next-js-dc480a88e0b8">History of Vercel 2015–2020 (6/7). Zeit and Next.js</a>;</li><li><strong>History of Vercel 2020-Present (7/7). Zeit is now Vercel.</strong></li></ol><h3>Rebranding</h3><p>First, it’s worth dwelling on what the company zeit represented. And to describe this is quite difficult — speaking Zeit implied directly the company itself, and often spoke about Zeit Now — for example, in one interview Guillermo was asked a question, starting with “Your products zeit and zeit now”.</p><p>The new company was supposed to unify these products. And today, saying “Vercel”, implies both the concept of Zeit and Zeit Now. “Our product consists of two parts: NextJS and a platform distributed around the world, and the business is based on the scalability of this platform” — Guillermo Rauch.</p><p>The decision to rebrand caused a surge of emotions in the community — a lot of negative comments, requests to return everything back and/or not to sell the company (<em>it is difficult to understand why many considered this rebranding a sale</em>).</p><p>In addition to this, there are more interesting reasons for the rebranding. So, Guillermo noted that the old name sounded differently in different languages and not everywhere it was easy to write what was heard — “The new name was analyzed in five languages by five linguists from different languages of the world”.</p><p>The new name — Vercel — was devised by the company <a href="https://www.lexiconbranding.com/">Lexicon</a>. According to them: “Zeit needed a name that would reflect the efficiency, superiority, and power of their platform. They also needed the name to be short, spread worldwide, and easy for developers to type command”.</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.canva.com%2Fdesign%2FDAF-DLcqQX4%2F2xQuSe4pfKj--1uTypLwng%2Fview%3Fembed%26meta&amp;display_name=Canva&amp;url=https%3A%2F%2Fwww.canva.com%2Fdesign%2FDAF-DLcqQX4%2F2xQuSe4pfKj--1uTypLwng%2Fedit%3Futm_content%3DDAF-DLcqQX4%26utm_campaign%3Ddesignshare%26utm_medium%3Dlink2%26utm_source%3Dsharebutton&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=canva" width="1920" height="1080" frameborder="0" scrolling="no"><a href="https://medium.com/media/027c9dc5378d549540fc89be653fa2bb/href">https://medium.com/media/027c9dc5378d549540fc89be653fa2bb/href</a></iframe><p>The business model as a whole remained unchanged. “Although anyone can access the library for free, the company’s business model is based on selling software as a service (SaaS) to companies” — Guillermo Rauch.</p><h3>Investments</h3><p>As mentioned earlier, the rebranding quickly paid off. Immediately after it, Vercel announced a successful round A — $21 million. Investors were: <a href="https://www.accel.com/">Accel</a>, mentioned <a href="https://www.crv.com/">CRV</a>, <a href="https://twitter.com/naval">Naval Ravikant</a>, <a href="https://twitter.com/natfriedman">Nat Friedman</a>, <a href="https://twitter.com/jordwalke">Jordan Walke</a> and others. Accel fund member — Daniel Levine — is a member of the Vercel board of directors.</p><p>The company went for the next round only after 8 months, in December 2020, and was able to raise $40 million. The biggest investor in this round was Google Ventures. Also, new investors were <a href="http://www.greenoakscap.com/">Greenoaks Capital</a>, <a href="http://www.bedrockcap.com/">Bedrock Capital</a> and <a href="https://www.geodesiccap.com/">Geodesic Capital</a>. Bedrock founder Geoff Lewis is also a member of the Vercel board of directors.</p><p>The next round took place in June 2021. The company raised $102 million, received a valuation of $1.1 billion, thus becoming a unicorn company. The main investor was the company <a href="http://www.bedrockcap.com/">Bedrock Capital</a>. All previous investors were retained and several new ones were added (<a href="https://8vc.com/">8VC</a>, <a href="https://www.flexcapital.com/">Flex Capital</a>, <a href="https://www.ggvc.com/">GGV</a>, <a href="https://www.latacora.com/">Latacora</a>, <a href="https://www.salesforce.com/company/ventures/">Salesforce Ventures</a>, and <a href="https://www.tigerglobal.com/">Tiger Global</a>).</p><p>The last round took place in November 2021, during which Vercel raised $150 million. The main investor was <a href="https://www.ggvc.com/">GGV Capital</a>, also all the previous investors supported the company (<em>including Accel, Bedrock, and Google Ventures</em>).</p><p>Private investments are also interesting. Guillermo, after leaving Automattic, became an investor himself and began investing mainly in emerging tech companies. Some of his seed investments were companies auth0 and scale, which soon became unicorns (Auth0 became the fifth with Argentine roots, that is, shortly before Vercel).</p><p>Soon after, their co-founders (Auth0 — Matias Woloski, scale — Alexandr Wang) — invested in Vercel.</p><p>The complete list of Vercel investors can be found on the <a href="https://vercel.com/about">company’s website</a>.</p><p>Total investments in Vercel amounted to $313 million. This allowed the company not only to actively develop and attract people but also to make some acquisitions.</p><h3>Purchases and Important Actions</h3><p>One of the most important and valuable actions of the company was the attraction of Rich Harris, the creator of svelte. This happened in November 2021, 2 weeks before the completion of the last round of investments (<em>and possibly played a significant role in the success of the round</em>).</p><p>So far, the company has made 2 major acquisitions: the company Splitbee and the utility turborepo. The cost of both purchases is not named.</p><p>First, Vercel bought turborepo, in December 2021 — a utility created by Jared Palmer (<em>he is also the creator of Formik and TSDX</em>). “Turborepo is a high-performance build system for JavaScript and TypeScript codebases”. The main features of the utility are parallel assembly of applications in a monorepo and caching processes (<em>including remote ones</em>).</p><p>Jared himself joined the Vercel team after purchasing the utility, his main task was to speed up the assembly in Vercel.</p><p>The next purchase took place almost a year later, in October 2022. The company Splitbee was purchased — a platform for collecting real-time analytics, created only in 2020.</p><p>The co-founders of the company joined the Vercel team, their main task was to develop analytics tools within Vercel (<em>in the service you can buy analytics collection, the price starts from $10 per project</em>).</p><h3>Open-Source Development</h3><p>“Supporting open-source projects is an <a href="https://vercel.com/blog/vercel-funding-series-d-and-valuation#support-open-source-projects">integral part of our mission</a> “Make the Internet Faster””.</p><p>Despite several purchases — the main improvements come through integrating third-party services. For example, there is integration with <a href="http://check.ly/">Check.ly</a>, which takes a URL and runs automatic end-to-end tests, simulating web browsers for this interface. Another interesting integration is made with Sanity CMS, together with them Vercel adds the possibility of editing the site on the site itself (<a href="https://next-preview.now.sh/">next-preview.now.sh</a>).</p><p>In total, Vercel has about 100 integrations (<em>Mongo DB, Contenful, Wix, Shopify, AWS, Sentry, Slack, Auth0, etc.</em>).</p><p>Vercel owns such frameworks and libraries as Next.js, Hyper, SWR, pkg, turbo, satori, serve, styled-jsx, ai, and other less popular utilities. Also, team members are authors of many libraries.</p><p>Next itself after the rebranding became 10 times more popular (<em>from half a million to 5 million downloads weekly</em>).</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*4wFio3thXxH0VUH0852ymA.png" /><figcaption>Popularity of next.js, <a href="https://www.npmtrends.com/next">npmtrends</a></figcaption></figure><p>Vercel is also a sponsor of Nuxt, Astro, webpack, Babel, NextAuth, Parcel, and Unified and other open-source projects.</p><h3>Team</h3><p>Today the company has more than 200 employees, only an office in San Francisco, but most of the team works remotely from all over the world. This situation has been in the company from the very beginning, as its parent, Zeit, was founded by an Argentine, a Finn, and a Japanese, who often worked from different places. “From the very beginning, we were a remote company. We worked together from San Francisco, Argentina, Brazil, Finland, Japan, and Germany. We were fortunate to use remote work before covid forced us” — Guillermo Rauch.</p><p>In hiring, the company adheres to the policy of Guillermo’s first projects — they attract active participants in open-source. Vercel is also worked on by the creator of Webpack and Turbopack Tobias Koppers, creator of Svelte Rich Harris, React developers — Sebastian Markbåge, Andrew Clark, and Josh Story.</p><p>Most of them are hired by the company with the expectation of continuing work on their projects. So, Sebastian still heads the main React team, but also helps support the development of React in Vercel.</p><p>Guillermo himself, in addition to Vercel, is involved in investing. In April 2022, he received a second citizenship — American.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*CcOZsdbD70XX9FpfAR8kDA.png" /></figure><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=c6fde0b931e6" width="1" height="1" alt=""><hr><p><a href="https://medium.com/history-of-vercel/history-of-vercel-2020-present-7-7-zeit-is-now-vercel-c6fde0b931e6">History of Vercel 2020-Present (7/7). Zeit is now Vercel</a> was originally published in <a href="https://medium.com/history-of-vercel">History of Vercel</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[History of Vercel 2015–2020 (6/7). Zeit and Next.js]]></title>
            <link>https://medium.com/history-of-vercel/history-of-vercel-2015-2020-6-7-zeit-and-next-js-dc480a88e0b8?source=rss-f9cb10da019e------2</link>
            <guid isPermaLink="false">https://medium.com/p/dc480a88e0b8</guid>
            <category><![CDATA[front-end-development]]></category>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[history-of-technology]]></category>
            <category><![CDATA[startup]]></category>
            <category><![CDATA[technology]]></category>
            <dc:creator><![CDATA[Alex Savelyev]]></dc:creator>
            <pubDate>Wed, 21 Feb 2024 11:57:30 GMT</pubDate>
            <atom:updated>2025-03-14T05:12:14.426Z</atom:updated>
            <content:encoded><![CDATA[<p>After leaving Automattic in 2015, Guillermo founded a new company — Zeit. Co-founders were Tony Kovanen and Naoyuki Kanezawa.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*hwenFAfmK-mbIV3rzGVNSA.jpeg" /></figure><p>The main task of the company was to provide developers and teams with the ability to easily develop, preview, and deploy their applications.</p><p>“One of my dreams is that the next Facebook or Snapchat will be created by someone who didn’t have to go through all this training, develop these connections, and hire these bright people. It could be a girl from Africa or a boy from Bangladesh” — Guillermo Rauch.</p><p>This part will discuss the most uncertain period in the history of Vercel — the time when the company was becoming known to the community and fading from investors’ view.</p><p>Zeit first became known thanks to its product <strong>Now</strong> — a tool for deploying applications with a single command from the terminal, allowing developers to easily assemble their projects and instantly share them.</p><p><strong>History of Vercel</strong></p><ol><li><a href="https://medium.com/history-of-vercel/history-of-vercel-annotation-41a62b0cf53c">History of Vercel. Annotation</a>;</li><li><a href="https://medium.com/history-of-vercel/history-of-vercel-1990-2009-guillermo-rauch-childhood-and-first-steps-in-programming-1dbf038ddf9a">History of Vercel (1/7). 1990–2009. Guillermo Rauch. Childhood and first steps in programming</a>;</li><li><a href="https://medium.com/history-of-vercel/history-of-vercel-2009-2013-first-startup-by-guillermo-rauch-learnboost-7da50d365f0f">History of Vercel (2/7). LearnBoost. A leading tech company</a>;</li><li><a href="https://medium.com/history-of-vercel/the-history-of-vercel-2009-2013-learnboost-team-and-open-source-27c63d12549a">History of Vercel (3/7). 2009–2013. LearnBoost. Team that has become a leader in open source</a>;</li><li><a href="https://medium.com/history-of-vercel/history-of-vercel-cloudup-0cb0c0899bbd">History of Vercel (4/7). 2013. Cloudup. Drag. Drop. Stream</a>;</li><li><a href="https://medium.com/history-of-vercel/history-of-vercel-2013-2015-5-7-automattic-5cf2c1b6c98a">History of Vercel 2013–2015 (5/7). Automattic</a>;</li><li><strong>History of Vercel 2015–2020 (6/7). Zeit and Next.js;</strong></li><li><a href="https://medium.com/history-of-vercel/history-of-vercel-2020-present-7-7-zeit-is-now-vercel-c6fde0b931e6">History of Vercel 2020-Present (7/7). Zeit is now Vercel</a>.</li></ol><h3>Now</h3><p>The utility’s purpose was simple: You type “now” in the command line and get a new server. This concept is basically inherited by Vercel and even described in its main slogan “Develop. Preview. Ship”.</p><p>After entering the command, within a second, a new instance of the application is created and published on the Internet. You can share the link to the published application immediately, even before the build is completed — initially, the build process will be displayed on it, and then the application itself.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*T0Agl5sjweHtXHP-moBGFA.png" /></figure><p>Guillermo described the procedure as follows:</p><ol><li>We take package.json, Dockerfile, or static files, publish them in the cloud, and serve them securely over HTTP/2.</li><li>Each deployment is <strong>immutable</strong> and has its unique URL. They look like this: <a href="https://rauchg-blog-hhafwnefgw.now.sh/.">https://rauchg-blog-hhafwnefgw.now.sh/.</a></li><li>To go into production, you just assign them domains. Simply, with one command: now alias rauchg-blog-hhafwnefgw.now.sh rauchg.com.</li></ol><p>An application for PC — “<strong>Now Desktop</strong>” was also created for the utility. The utility itself supported the publication of static sites, next.js applications, as well as applications on go, php, node.js, python, rust, and other options.</p><p>Next, a global DNS solution — “Zeit World” was created.</p><h3>HyperTerm</h3><p>The Now utility had its page on the company’s website. At the very beginning of this page, there was a demo of its work in the terminal. And what is interesting here is not so much an example of work, but the fact that the terminal was added not as a gif, but written in pure html, css, and js.</p><p>Guillermo liked the demo result, as it looked like the simplest terminal. Then he thought about a new project — Hyper.app. The development of the first version took about two weeks. The terminal, like most other company projects, was published in open access and immediately after a quick presentation attracted the attention of developers — they began to actively participate in the development of the utility and soon more than 100 plugins were written for the terminal.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*aKlzsanrcgBV62OABq4Afw.png" /></figure><p>HyperTerm itself was created on Electron. This allowed you to open the developer console at any time and make various changes. You can also install ready-made plugins, many of which are collected in a special <a href="https://github.com/bnb/awesome-hyper">list</a>.</p><p>The terminal is still highly popular (<a href="https://github.com/vercel/hyper">gihub</a>, <a href="https://hyper.is/">website</a>).</p><h3>Next.js</h3><p>In 2016, Zeit released <strong>Next.js</strong> — a framework for creating Jamstack-style websites. The framework page lists the authors: Tim Neutkens, Naoyuki Kanezawa, Guillermo Rauch, Arunoda Susiripala, Tony Kovanen, Dan Zajdband. All but Dan worked at Zeit. Dan is familiar with Guillermo from JSConf Argentina, and probably as a developer of The Lift company (which used cloudUp, Guillermo’s second startup).</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*_v7Y5navGK56ut-dx_VmzQ.png" /></figure><p>Next.js was initially released as an open source project on GitHub on October 25, 2016. The framework offers out-of-the-box server-side rendering, static site generation, API routes, and more. The goal was to provide critical features that React lacks — primarily in terms of speed and SEO optimization.</p><p>“We work for those who do front-end design to make e-commerce sites, media and everything else better… Everything should look good, sites should load quickly” — Guillermo Rauch</p><p>Today, Next.js is used by Uber, Amazon, Open AI, and thousands of other companies. Being a framework for React, Next.js has become an important part of the ecosystem — “NextJS takes it to a new level. And now many ideas from NextJS inspire React itself”.</p><h3>Zeit Platform</h3><p>Despite the fact that applications were published through the Now utility — it was just a part of the Zeit platform.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*054pwEq4pGkc2nlStbdOZQ.png" /></figure><p>After entering the platform, you could publish up to 3 applications for free through Now.</p><p>However, you could not link domains in the free tariff. This was a paid feature, the price started from $15, and, for example, support cost from $750 to $2000. Also, you could buy domains directly in the platform.</p><p>The pricing policy was changed in December 2018 and, most importantly, limits on the number of applications and linked domains were removed, and for many categories, the price was calculated based on the resources spent over the limits. It lasted until the end of 2019, when it was changed again and fixed tariffs from $20 appeared.</p><h3>Co-founders</h3><h3>Guillermo Rauch</h3><p>In addition to the utilities mentioned in the article, Guillermo participated a lot in conferences and presented Now and Next.js. Also during this period, he was a mentor for the “Open Source Engineering” course organized by Stanford.</p><h3>Tony Kovanen</h3><p>Tony worked with Guillermo at Automattic and mainly worked on the Jetpack plugin. He left Automattic with Guillermo and became a co-founder of Zeit. In it, he will hold the position of CTO.</p><p>At Zeit, Tony participated in the development of next.js and Now. He worked until 2017, after which he will move to Gatsby. Now Tony is working on the <a href="http://based.io/"><strong>Based.io</strong></a> platform.</p><h3>Naoyuki Kanezawa</h3><p>Naoyuki was one of the main developers of <a href="https://socket.io/">Socket.IO</a> and <a href="https://www.npmjs.com/package/engine.io">Engine.IO</a> (<em>created by Guillermo</em>).</p><p>Now Naoyuki remains part of the Vercel team in the position of Infrastructure/Backend Developer.</p><h3>Team</h3><p>From the team it is worth noting:</p><p>Tim Neutkens, creator of <a href="https://github.com/vercel/micro">Micro</a> and <a href="https://mdxjs.com/">MDX</a>;</p><p>Arunoda Susiripala, creator of React Storybook;</p><p>Igor Klopov, creator of Pkg;</p><p>Nathan Rajlich, creator of node-gyp;</p><p>Javi Velasco, creator of React Toolbox.</p><p>Nicolas Garro / Evil Rabbit, Founding Designer and Brand Architect.</p><h3>Zeit Day</h3><p>Also, the Zeit company organized one-day conferences — “Zeit day”, the first of which took place in 2017.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ZOVQGXFQuyF9HF6odwZ-xw.jpeg" /><figcaption>Zeit Day. From left to right: Naoyuki Kanezawa, Arunoda Susiripala, Mateus Fernandes, Nicolas Garro, Jarmo Isotalo, Nathan Rajlich, Olli Vanhoja, presumably Igor Klopov, Guillermo Rauch</figcaption></figure><h3>Investments</h3><p>Zeit can be called a failure in terms of investments — in essence, the company was not perceived as a startup and this was its main problem. So, one of the investors in the future Vercel company — CRV — said when investing in the latter that they are “glad to resume business with Guillermo, after he returned to the path of entrepreneurship and founded Vercel”.</p><p>Actually, this was one of the main reasons for the rebranding that happened. And soon Vercel will become Guillermo’s first unicorn (<em>and the sixth unicorn with Argentine roots</em>).</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=dc480a88e0b8" width="1" height="1" alt=""><hr><p><a href="https://medium.com/history-of-vercel/history-of-vercel-2015-2020-6-7-zeit-and-next-js-dc480a88e0b8">History of Vercel 2015–2020 (6/7). Zeit and Next.js</a> was originally published in <a href="https://medium.com/history-of-vercel">History of Vercel</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[History of Vercel 2013–2015 (5/7). Automattic]]></title>
            <link>https://medium.com/history-of-vercel/history-of-vercel-2013-2015-5-7-automattic-5cf2c1b6c98a?source=rss-f9cb10da019e------2</link>
            <guid isPermaLink="false">https://medium.com/p/5cf2c1b6c98a</guid>
            <category><![CDATA[web-development]]></category>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[startup]]></category>
            <category><![CDATA[front-end-development]]></category>
            <category><![CDATA[website]]></category>
            <dc:creator><![CDATA[Alex Savelyev]]></dc:creator>
            <pubDate>Wed, 14 Feb 2024 10:43:20 GMT</pubDate>
            <atom:updated>2026-04-13T20:43:03.294Z</atom:updated>
            <content:encoded><![CDATA[<p>Automattic. A company that played a massive role in shaping the modern internet and deserves a separate series of articles. However, it will only be touched on superficially here.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*dKpk4U2-69MalYG1syR3og.jpeg" /></figure><p>In May 2003, Matt Mullenweg, together with Michael Little, founded a new platform for publishing blogs. It was not the first such platform; the most popular at the time was b2/cafelog. Indeed, Matt and Michael were the developers of that platform, but they decided to create their own product. The new platform developed rapidly and quickly gained a large audience.</p><p>This part will discuss what Guillermo Rauch and the Learnboost team did after Automattic acquired them.</p><p><strong>History of Vercel</strong></p><ol><li><a href="https://medium.com/history-of-vercel/history-of-vercel-annotation-41a62b0cf53c">History of Vercel. Annotation</a>;</li><li><a href="https://medium.com/history-of-vercel/history-of-vercel-1990-2009-guillermo-rauch-childhood-and-first-steps-in-programming-1dbf038ddf9a">History of Vercel (1/7). 1990–2009. Guillermo Rauch. Childhood and first steps in programming</a>;</li><li><a href="https://medium.com/history-of-vercel/history-of-vercel-2009-2013-first-startup-by-guillermo-rauch-learnboost-7da50d365f0f">History of Vercel (2/7). LearnBoost. A leading tech company</a>;</li><li><a href="https://medium.com/history-of-vercel/the-history-of-vercel-2009-2013-learnboost-team-and-open-source-27c63d12549a">History of Vercel (3/7). 2009–2013. LearnBoost. Team that has become a leader in open source</a>;</li><li><a href="https://medium.com/history-of-vercel/history-of-vercel-cloudup-0cb0c0899bbd">History of Vercel (4/7). 2013. Cloudup. Drag. Drop. Stream</a>;</li><li><strong>History of Vercel 2013–2015 (5/7). Automattic;</strong></li><li><a href="https://medium.com/history-of-vercel/history-of-vercel-2015-2020-6-7-zeit-and-next-js-dc480a88e0b8">History of Vercel 2015–2020 (6/7). Zeit and Next.js</a>;</li><li><a href="https://medium.com/history-of-vercel/history-of-vercel-2020-present-7-7-zeit-is-now-vercel-c6fde0b931e6">History of Vercel 2020-Present (7/7). Zeit is now Vercel</a>.</li></ol><h3>Automattic</h3><p>On May 27, 2003, Matt announced the availability of the first version of the new platform and named it “WordPress”.</p><p>The main task of Automattic itself (<em>besides developing WordPress</em>) became hosting and supporting websites written in WordPress. The main principle of monetization was that any user could create a site on WordPress, publish it for free, and then, if necessary, pay for additional features.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1003/1*VFqkSVAw7Fw8pfEV9uYV-A.png" /><figcaption>One of the first versions of WordPress, 2004.</figcaption></figure><p>Automattic raised 6 million in Series A and B funding rounds from investors including True Ventures, Polaris Venture Partners, The New York Times, and others. In May 2013, Tiger Global invested 50 million dollars in purchasing shares from Automattic’s early investors. By that time, about 20% of websites were running on WordPress.</p><p>Automattic actively purchased interesting products, primarily due to the people working on them. In 2013, they met with Guillermo and Tian Lu.</p><h3>Sale of CloudUp</h3><p>On September 25, 2013, after the meeting, Automattic announced the purchase of CloudUp, along with the company LearnBoost and all related libraries (<em>including socket and mongoose</em>).</p><p>This was Automattic’s 12th acquisition (<em>after </em><a href="https://beta.techcrunch.com/2013/07/15/automattic-acqui-hires-lean-domain-search-to-improve-its-domain-registration-service/"><em>Lean Domain Search</em></a><em>, </em><a href="https://beta.techcrunch.com/2013/06/17/automattic-acquires-ios-wordpress-client-poster-to-improve-its-own-mobile-apps/"><em>Poster</em></a><em>, </em><a href="https://beta.techcrunch.com/2013/01/24/wordpress-simperium-simplenote/"><em>Simperium</em></a><em>, CodeGarage, </em><a href="https://beta.techcrunch.com/2009/09/08/automattic-acquires-spellcheck-plug-in-after-the-deadline/"><em>After the Deadline</em></a><em>, </em><a href="http://blo.gs/"><em>Blo.gs</em></a><em>, </em><a href="https://beta.techcrunch.com/2008/10/15/wordpress-acquires-irish-startup-polldaddy/"><em>PollDaddy</em></a><em>, </em><a href="https://beta.techcrunch.com/2008/09/23/automattic-has-acquired-intensedebates-enhanced-comment-system/"><em>IntenseDebate</em></a><em>, BuddyPress, </em><a href="https://beta.techcrunch.com/2007/10/17/automattic-acquires-gravatar/"><em>Gravatar</em></a><em> and </em><a href="https://beta.techcrunch.com/2010/06/25/automattic-buys-up-thing-labs-plinky-to-help-bloggers-overcome-writers-block/"><em>Plinky</em></a>).</p><p>The CloudUp team set about updating the editor and tools related to media in WordPress. Matt himself (<em>the founder of WordPress</em>) admitted that CloudUp was significantly better than the WordPress media library. The editor was planned to implement real-time editing for simultaneous work on texts by several people.</p><h3>Life at Automattic</h3><p>The LearnBoost and Automattic teams had a similar view on open development — they participated in conferences, created and supported open-source projects. “Automattic and us share a history and vision: we have a distributed workforce, we passionately care about creating a better web and we support our open-source roots” — Tian Lu (<em>co-founder of CloudUp</em>).</p><p>The team took on the tasks set for them, but they did not plan to stop working on CloudUp — they created new projects, packages, and talked about the imminent expansion of the service.</p><p>They also continued to work on open-source projects. Thus, in 2014, the first stable version of <a href="http://socket.io/">socket.io</a> was introduced.</p><p>Nevertheless, the story of the CloudUp service itself ended there. The site remains unchanged and continues to lie on the internet with a field for applying to join the testing.</p><h3>The Team’s Future</h3><h4>Tian Lu</h4><p>After the company’s acquisition, Tian remained the general manager of Cloudup, overseeing the creation of a new technology stack and a completely new editor.</p><p>Also, in 2013, Tian will create his company Tsukemen. As a co-founder of two startups, Tian raised nearly 5 million dollars from CRV, Bessemer, RRE, and other investors before a successful exit.</p><p>Now, Tian is a Vice President of Product at <a href="http://blockchain.com/">Blockchain.com</a>, responsible for product strategy and design. He joined the <a href="http://blockchain.com/">Blockchain.com</a> team through the acquisition of his company Tsukemen.</p><h4>Nathan Rajlich</h4><p>Since 2013, Nathan has been working at the WordPress company on editors.</p><p>In 2014, he spoke at a conference in Buenos Aires on the topic of “Writing a webmodule” — about writing npm modules intended for use in browsers [<a href="https://www.youtube.com/watch?v=5EFAnFske_w">speech</a>].</p><p>Until 2016, he worked at Cloudup and Automattic. He currently works at Vercel.</p><h4>Tj Holowaychuk</h4><p>In 2014, while already working at <a href="http://segment.com/">segment.com</a> as a backend developer, he will participate in the development of YAL. In 2014, TJ will write the article “<a href="https://medium.com/@tjholowaychuk/farewell-node-js-4ba9e7f3e52b">Farewell Node.js</a>” (<em>goodbye node.js</em>) with an official farewell to node.js and a transition to the Go language. However, he continues to support his projects on js (<em>primarily koa</em>). In 2014, TJ will sell express to StrongLoop (<em>but that’s a whole other story</em>).</p><p>This was not just a departure from Node.js, but also a partial departure from programming and open-source. The following are TJ’s words:</p><p>“No, I have no intentions, my new goal is to live better. After all, open-source doesn’t pay the bills, so it’s better to focus on other things or if you just like a project, that’s cool.</p><p>Now I spend most of my time enjoying other things, I code for 2–3 hours a day if I don’t like something. Time is your real currency! Money is good, but don’t waste time. If you really like the project you’re working on, then do it, but don’t neglect other areas of your life (<em>or people</em>).”</p><p>In 2016, TJ will create the Apex company, and Guillermo — the Now utility and the Zeit company.</p><h4>Guillermo Rauch</h4><p>Guillermo also worked on the development of WordPress, for example, he redesigned the video platform — VideoPress. For this, he “used the Virtual DOM approach and wrote a very simple version of React,” making it interactive and convenient. He added features such as searching for a moment by frames and embedding video functionality.Guillermo left WordPress on October 13, 2015, and founded a new company — Zeit.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=5cf2c1b6c98a" width="1" height="1" alt=""><hr><p><a href="https://medium.com/history-of-vercel/history-of-vercel-2013-2015-5-7-automattic-5cf2c1b6c98a">History of Vercel 2013–2015 (5/7). Automattic</a> was originally published in <a href="https://medium.com/history-of-vercel">History of Vercel</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[More libraries to the library god or how I revisited i18n]]></title>
            <link>https://alexdln.medium.com/how-i-revisited-i18n-47470ef71c91?source=rss-f9cb10da019e------2</link>
            <guid isPermaLink="false">https://medium.com/p/47470ef71c91</guid>
            <category><![CDATA[web-development]]></category>
            <category><![CDATA[nextjs]]></category>
            <category><![CDATA[front-end-development]]></category>
            <category><![CDATA[frontend]]></category>
            <category><![CDATA[react]]></category>
            <dc:creator><![CDATA[Alex Savelyev]]></dc:creator>
            <pubDate>Tue, 13 Feb 2024 10:16:34 GMT</pubDate>
            <atom:updated>2024-02-13T11:07:19.213Z</atom:updated>
            <content:encoded><![CDATA[<h3>More libraries to the library god or how I remade i18n [next.js v14]</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*tKR1JadkKT8I5oVw" /></figure><p>There are dozens of amazing libraries made for internationalization, such as i18n, react-intl, next-intl. They all do an excellent job of adding translations to an application or website. Most of them are tested, debugged, and consistently supported.</p><p>But they are all outdated.</p><p>After all, during this time, the react ecosystem has been developing. The latest version of next.js has major updates from react.js — cache, taint, new hooks, and, of course, server components. The React.js team will likely introduce these changes in May.</p><p>In this article, I will talk about key changes, personal experience, problems with existing solutions, necessary updates, solutions I came up with, and, of course, answer the questions of “why” and most importantly — “why”?</p><h3>Changes</h3><p>The first thing to start with is how the changes in React.js made translation libraries obsolete.</p><p>Despite the fact that the latest stable version of React.js was released almost two years ago, it has 2 other channels — canary and experimental, where canary is also considered a stable channel and is recommended for use by libraries.</p><p>This is the channel that Next.js uses. Next.js launched server components without additional flags inside the so-called App Router — a new directory as an alternative to pages, which uses its conventions and different sugar (<em>about changes and problems which I wrote in a recent </em><a href="https://medium.com/@vordgi/next-js-app-router-experience-of-use-path-to-the-future-or-wrong-turn-277dde4369a6"><em>article</em></a>).</p><p>Server components definitely solve a number of problems and are a new milestone for optimizations. Including for translations. Without server components, translations were stored both in the compiled HTML and as a large object in the client script. Now you can get ready-made HTML, which doesn’t need anything on the client.</p><p>Next.js paid special attention to this feature.</p><h3>Personal experience</h3><p>You can add translations (<em>according to the </em><a href="https://nextjs.org/docs/app/building-your-application/routing/internationalization"><em>Next.js documentation</em></a>) as follows:</p><pre>// app/[lang]/dictionaries.js<br>import &#39;server-only&#39;<br><br>const dictionaries = {<br>  en: () =&gt; import(&#39;./dictionaries/en.json&#39;).then((module) =&gt; module.default),<br>  nl: () =&gt; import(&#39;./dictionaries/nl.json&#39;).then((module) =&gt; module.default),<br>}<br>export const getDictionary = async (locale) =&gt; dictionaries[locale]()</pre><pre>// app/[lang]/page.js<br>import { getDictionary } from &#39;./dictionaries&#39;<br><br>export default async function Page({ params: { lang } }) {<br>  const dict = await getDictionary(lang) // en<br>  return &lt;button&gt;{dict.products.cart}&lt;/button&gt; // Add to Cart<br>}</pre><p>This solution is described as ready and fully optimized. It works entirely on the server, and the client already receives ready-made HTML. However, the Next.js team missed one important detail — how to pass the language deep into server components.</p><p>A big problem with server components is that contexts are not available in them. The Next.js team explains the absence of these functions by the fact that Layout is not re-rendered, and everything that depends on props should be client-side.</p><p>Perhaps translation libraries were most affected by this. As a temporary solution, they suggest determining the language in the middleware and adding it to cookies. Then when building the page, read it in the necessary places. But reading cookies means enabling server rendering, which is not suitable for everyone.</p><p>In general, the main problem with existing solutions is that most of them are not made for server components. Components and functions were developed for runtime, using hooks and synchronicity.</p><p>Another inconvenience was caching in Next.js. Namely — it works fully only for GET requests, and if the weight of translations is more than the limit of 2MB — they will not be cached.</p><h3>Implementation</h3><p><strong>Goals and tasks:</strong></p><ul><li>The library must have full functionality in both client and server components;</li><li>Usage should be simple (<em>without extra props</em>);</li><li>All complex logic should be moved to the server;</li><li>Translations should be loaded only as necessary and without additional requests;</li><li>Support for updating translations without rebuilding (<em>ISR/SSR)</em>;</li><li>Everything should work on a static site (<em>not with switching to SSR</em>);</li><li>Html entities should be supported.</li></ul><p>To my surprise, there is not a single library that would satisfy all these requirements.</p><p>The first thing you need is functionality. In the standard version, this is a hook that returns the function t and the Trans component for more complex translations. However, such functionality is needed in server components, and they have many of their own features.</p><h3>Functionality</h3><p>The main functionality is divided into two versions — for client components and for server ones and includes:</p><p>useTranslation, getTranslation - which return the function t inside the DOM and the language;</p><pre>import getTranslation from &#39;next-translation/getTranslation&#39;<br><br>export default function ServerComponent() {<br>  const { t } = getTranslation()<br><br>  return (<br>    &lt;p&gt;{t(&#39;intro.title&#39;)}&lt;/p&gt;<br>  )<br>}</pre><pre>&#39;use client&#39;;<br><br>import useTranslation from &#39;next-translation/useTranslation&#39;<br><br>export default function ClientComponent() {<br>  const { t } = useTranslation()<br><br>  return (<br>    &lt;p&gt;{t(&#39;intro.title&#39;)}&lt;/p&gt;<br>  )<br>}</pre><p>The interface turned out to be quite familiar, the functions support namespace and query. It is recommended to use it by default, as it is simple to use and in logic. Returns a ready-made string.</p><p>For more complex translations, you should use the ClientTranslation and ServerTranslation components. They can replace pseudo-components with real ones.</p><pre>import ServerTranslation from &#39;next-tranlation/ServerTranslation&#39;;<br><br>export default function ServerComponent() {<br>  return(<br>    &lt;ServerTranslation<br>      term=&#39;intro.description&#39;<br>      components={{<br>        link: &lt;a href=&#39;#&#39; /&gt;<br>      }}<br>    /&gt;<br>  )<br>}</pre><pre>&quot;use client&quot;;<br><br>import ClientTranslation from &#39;next-tranlation/ClientTranslation&#39;;<br><br>export default function ClientTranslation() {<br>  return(<br>    &lt;ClientTranslation<br>      term=&#39;intro.description&#39;<br>      components={{<br>        link: &lt;a href=&#39;#&#39; /&gt;<br>      }}<br>    /&gt;<br>  )<br>}</pre><p>There are also cases when translations need to be added outside the react tree. For this, you can use createTranslation anywhere.</p><pre>import createTranslation from &#39;next-translation/createTranslation&#39;<br>// ...<br>export async function generateMetadata({ params }: { params: { lang: string } }) {<br>  const { t } = await createTranslation(params.lang);<br><br>  return {<br>    title: t(&#39;homePage.meta.title&#39;),<br>  }<br>}</pre><h3>Page setup</h3><p>Now about setting up the page. To work with translations, you need to know the language. However, in server components, you cannot use context. For this, an alternative to createContext was made for server components in the <a href="https://github.com/vordgi/next-impl-getters">next-impl-getters</a> package - createServerContext and getServerContext.</p><p>In the package for this, you need to create a NextTranslationProvider. It is recommended to do this at the page level to avoid problems with Layout re-rendering.</p><pre>import NextTranlationProvider from &#39;next-translation/NextTranlationProvider&#39;<br><br>export default function HomePage({ params }: { params: { lang: string } }) {<br>  return (<br>    &lt;NextTranlationProvider lang={params.lang} clientTerms={[&#39;shared&#39;, &#39;banking.about&#39;]}&gt;<br>      {/* ... */}<br>    &lt;/NextTranlationProvider&gt;<br>  )<br>}</pre><p>It is also necessary to indicate which translations are needed specifically on the client and to pass only them there. To do this, you can pass an array of client keys or groups to NextTranslationProvider using the clientTerms prop.</p><p>Also, sometimes situations arise when a component needs different translations or different blocks are rendered depending on conditions. In such cases, different translations need to be passed to the client. Condition options can be wrapped in NextTranslationTransmitter and client terms are passed to it.</p><pre>import NextTranslationTransmitter from &#39;next-tranlation/NextTranslationTransmitter&#39;;<br>import ClientComponent from &#39;./ClientComponent&#39;;<br><br>const ServerComponent: React.FC = () =&gt; (<br>  &lt;NextTranslationTransmitter terms={[&#39;header.nav&#39;]}&gt;<br>    &lt;ClientComponent /&gt;<br>  &lt;/NextTranslationTransmitter&gt;<br>)</pre><p>As a result, only those terms that were specified above in NextTranslationProvider or NextTranslationTransmitter will be passed to client components.</p><h3>Package setup</h3><p>Before working with translations, they need to be loaded. For this, you need to create a configuration file in the root of the project. Its minimum configuration is the load function, which will return current translations and an array of languages with permissible languages. The load function is called in server components, and the necessary keys will be passed to the client.</p><p>A very important point was the absence of unnecessary requests, that is, full caching is needed.</p><p>Here it is worth digressing a little. Starting from the latest version, Next.js builds the application in parallel in several processes. If each process lived with its own cache — requests would be sent from each. Probably, to avoid this, the Next.js team redesigned fetch — now it works with a common cache.</p><p>The package solves the problem in the same way — it creates a common cache and works from each process already with it. For this to work, you need to use withNextTranslation in next.config.js.</p><h3>Conclusion</h3><p>The solution turned out to be truly tailored to next.js — taking into account all its capabilities and problems. It also includes all the optimization capabilities provided by server components. The package is fully optimized for next.js, their concepts and views, which I fully share.</p><p>I faced the problem of translations and I had to make my own solution, which would work exactly as expected.</p><p>Despite the significant advantage in optimizations, the package is still inferior to large libraries in terms of translation capabilities. There is a lot of work ahead.</p><p><em>P.S. I will be grateful if you describe what you lacked in existing solutions or what functionality you consider most important.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=47470ef71c91" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[History of Vercel (4/7). 2013. Cloudup. Drag. Drop. Stream.]]></title>
            <link>https://medium.com/history-of-vercel/history-of-vercel-cloudup-0cb0c0899bbd?source=rss-f9cb10da019e------2</link>
            <guid isPermaLink="false">https://medium.com/p/0cb0c0899bbd</guid>
            <category><![CDATA[front-end-development]]></category>
            <category><![CDATA[web-development]]></category>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[website]]></category>
            <category><![CDATA[startup]]></category>
            <dc:creator><![CDATA[Alex Savelyev]]></dc:creator>
            <pubDate>Wed, 07 Feb 2024 10:31:41 GMT</pubDate>
            <atom:updated>2024-08-15T21:03:54.683Z</atom:updated>
            <content:encoded><![CDATA[<p>Cloudup is a clear and fast file-sharing service for files, videos, links, music, documents, code, text, and so on, which is both user-friendly and recipient-friendly.</p><p>Drag.</p><p>Drop.</p><p>Stream.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*OSJXXxbCITaXKONWLSmZvg.jpeg" /></figure><blockquote>“We created Cloudup because we were disappointed with sharing services that took too much time for conversion, storage, management, and uploading — we just wanted to share anything, anytime.” — CloudUp team.</blockquote><p>In this part, I want to talk about the history and reasons for creating the service and what came out of it.</p><p><strong>History of Vercel</strong></p><ol><li><a href="https://medium.com/history-of-vercel/history-of-vercel-annotation-41a62b0cf53c">History of Vercel. Annotation</a>;</li><li><a href="https://medium.com/history-of-vercel/history-of-vercel-1990-2009-guillermo-rauch-childhood-and-first-steps-in-programming-1dbf038ddf9a">History of Vercel (1/7). 1990–2009. Guillermo Rauch. Childhood and first steps in programming</a>;</li><li><a href="https://medium.com/history-of-vercel/history-of-vercel-2009-2013-first-startup-by-guillermo-rauch-learnboost-7da50d365f0f">History of Vercel (2/7). LearnBoost. A leading tech company</a>;</li><li><a href="https://medium.com/history-of-vercel/the-history-of-vercel-2009-2013-learnboost-team-and-open-source-27c63d12549a">History of Vercel (3/7). 2009–2013. LearnBoost. Team that has become a leader in open source</a>;</li><li><strong>History of Vercel (4/7). 2013. Cloudup. Drag. Drop. Stream;</strong></li><li><a href="https://medium.com/history-of-vercel/history-of-vercel-2013-2015-5-7-automattic-5cf2c1b6c98a">History of Vercel 2013–2015 (5/7). Automattic</a>;</li><li><a href="https://medium.com/history-of-vercel/history-of-vercel-2015-2020-6-7-zeit-and-next-js-dc480a88e0b8">History of Vercel 2015–2020 (6/7). Zeit and Next.js</a>;</li><li><a href="https://medium.com/history-of-vercel/history-of-vercel-2020-present-7-7-zeit-is-now-vercel-c6fde0b931e6">History of Vercel 2020-Present (7/7). Zeit is now Vercel</a>.</li></ol><h3>Reasons</h3><p>The LearnBoost educational platform, Guillermo Rauch’s first startup, had already become popular and secured its place in the market by this time. It was a simple, convenient and fast service that solved all problems related to education. However, the interests and needs of teachers did not end there — they wanted to share their work, publish lesson plans, share photos from lessons in blogs, and talk about their work on social networks.</p><p>The LearnBoost development team also shared a large amount of information — images, videos, documents, presentations — for personal or work purposes. Different tools were used for each task.</p><p>LearnBoost users needed a tool that would allow them to share files with all network users. In existing solutions, you had to log in to view the file. Another disadvantage was the lack of cross-browser compatibility.</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fplayer.vimeo.com%2Fvideo%2F910533882%3Fapp_id%3D122963&amp;dntp=1&amp;display_name=Vimeo&amp;url=https%3A%2F%2Fplayer.vimeo.com%2Fvideo%2F910533882&amp;image=https%3A%2F%2Fi.vimeocdn.com%2Fvideo%2F1794556227-3c743c6dc298eecb25f8df96668d74a290b0fa507c7b3b1eeb5c029398b34f05-d_295x166&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=vimeo" width="400" height="300" frameborder="0" scrolling="no"><a href="https://medium.com/media/6fba085b598ec59d2784eaed03a04618/href">https://medium.com/media/6fba085b598ec59d2784eaed03a04618/href</a></iframe><h3>Competition</h3><p>CloudUp was not the first to enter the market in this area. By that time, there were already startups in the data storage sphere (<a href="https://www.wetransfer.com/">WeTransfers</a> and <a href="https://www.yousendit.com/">YouSendIts</a>), big players (Google Drive and Dropbox), and even social networks (Facebook, Twitter) were quite good at this task. Nevertheless, the goal of CloudUp was slightly different — not just to store files, but also to share them. But there were competitors at that time also — <a href="https://droplr.com/hello">DropLr</a>, <a href="http://getcloudapp.com/">CloudApp</a>, <a href="http://ge.tt/">Ge.tt</a>.</p><p>The most advanced service at that time was Dropbox, but its main task was (<em>and remains to this day</em>) data storage, not file sharing and distribution.</p><p>On June 20, 2013, Guillermo and Tian Lu announced the creation of a new file-sharing service — CloudUp. From the LearnBoost team, TJ, Meredith, and Nathan also participated in the development of cloudup.</p><h3>Tasks</h3><p>The main goal was to make the sharing of images, links, videos, code, documents, and everything else — simple and beautiful, both for service users and for those with whom these users share.</p><p>To do this, the following tasks needed to be solved:</p><ul><li>Cross-browser compatibility The service should be accessible from any device, with any OS and from any browser. The problem of video format support added complexity.</li><li>Grouping It was necessary to save files not in one endless list, but to make it possible to group them into categories.</li><li>Viewing without registration To simply view photos, it was not necessary to register on the service. As viewing is possible without registration, functionality for secure file sharing was needed.</li><li>Editing It was necessary to edit, change, delete or hide already uploaded files.</li><li>Speed For viewing files and videos, it was not necessary to wait for their full upload.</li></ul><h3>Functionality</h3><p>Drag. Drop. Stream. Three simple steps for files to take a huge path from the user’s device to viewers.</p><p>The service absorbed all the experience accumulated by the team, was built on modern technologies (<em>many of which were created and developed by the LearnBoost team itself</em>), a minimalist design was created for it. But this was not enough to stand out among the existing giants and become a market leader. The service needed to stand out, improve all existing solutions, and create new ones.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*1dh5d66Eni4RsysxKfzbPQ.png" /><figcaption>Cloudup interface</figcaption></figure><h3>Cross-platform and cross-browser compatibility</h3><p>Google Chrome only surpassed IE in popularity in 2012. By the time of the CloudUp launch (<em>June 2013</em>), cross-browser compatibility was a big and important task. In addition to directly supporting all browsers, the service also solved the problem of file format support. The service converted documents into PDF, performed video transcoding, and compressed images for devices with weak internet connections.</p><h3>Grouping and preview</h3><p>Existing alternatives displayed folders in a “list” format, CloudUp did so in a “tile” format and called this not folders, but streams. Streams differed from folders not only in their display style but also in their content.</p><p>In the “folders” of competitors, files were stored as a list, with basic icons. Streams, on the other hand, contained tiles with files that allowed you to see all the content in preview mode, be it a document, photo, file, or video. Thus, in these streams, you could easily and quickly find the necessary files. This worked with a really long list of files, whether they were simple documents, raw photos, or even psd, eps, and AI files, preview was available for everything.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*mqK9mL2uGtW-sX3NSVyViA.png" /><figcaption>Example of previewing a psd file. <a href="https://cloudup.com/c2Hw2pvsbNf">Try it</a>.</figcaption></figure><p>Also, directly from the service, you could view the meta-information about the file, such as weight, extension, creation date, resolution, and much more.</p><h3>Editing and security</h3><p>Each stream was assigned a unique identifier that could not be forged or predicted, so the only way to view a specific stream was to get a link. For additional stream protection, it could be password-protected.</p><p>At any time, the file could be edited, replaced, password added or removed, or deleted.</p><h3>Speed</h3><p>One of the main features of LearnBoost was real-time operation. The company that created the first library for these purposes — <a href="http://socket.io/">socket.io</a> was a leader in this area. The next startup was no exception, all files were uploaded and available in real time. As soon as the user started uploading files, a link would be immediately available to him, which he could share with his friends. The upload did not occur as a whole file, but as a stream, which was a significant advantage, primarily for video.</p><p>Also, the product followed the ideas of web 2.0, that is, it was a single-page application. <a href="https://cloudup.com/blog/the-need-for-speed">https://cloudup.com/blog/the-need-for-speed</a></p><h3>Application</h3><p>Along with the presentation of the service, an application for it was immediately presented. Initially only for OSX, but in the near future, apps for other platforms were planned.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*JIHMj76U-MgdXAl1bRi36w.png" /><figcaption>CloudUp mobile application</figcaption></figure><p>In addition to the main functionality, the application could also:</p><ul><li>Automatically upload all screenshots taken to the service;</li><li>Upload by transferring the necessary files to the CloudUp icon in the status bar. The link to the created stream was immediately copied to the clipboard and it could be immediately shared with friends or colleagues.</li></ul><p>And these promises were kept. Almost immediately after the launch, a Windows application was presented.</p><p>The application could also track screenshots and automatically upload them.</p><h3>Result</h3><p>On June 20, 2013, the service launched pre-registration. The service offered users 1000 files up to 200MB each for free. In early September, the service launched in beta testing and sent out 10,000 invitations. In just a few weeks, users uploaded over 300,000 files, the total volume of which was almost 1,500 GB.</p><p>CloudUp quickly and confidently entered the market for cloud solutions, but to secure a strong position in it, the team had to do a lot of work. The service had a huge and unique functionality, which favorably distinguished it from competitors, but this was not enough for establishment. The next step was to consider monetization.</p><p>The free tariff provided for the upload of up to 1000 files, which was enough for an ordinary user. For business, special tariffs, applications for all platforms, and collaborative work on files in real time were needed. The company continuously improved its applications, the performance of the service, supported file formats, thereby gradually closing all market needs.</p><p>The first company to fully switch to CloudUp for internal purposes was The Lift, on August 12, 2013 (<em>one and a half months later</em>). <a href="http://thelift.net/">theLift</a> was engaged in product development from concept to product, from software development to the creation of marketing materials. The company’s headquarters were located in Southern California, and there was also an office in Buenos Aires.</p><p>The second company was Sawhorse. <a href="http://www.sawhorsela.com/">Sawhorse</a> is a full-service production company helping startups and large companies share their stories through video and post-production campaigns. This, perhaps, was the first company well familiar with the product and the team, as they made the introductory video for CloudUp.</p><p>Also, CloudUp launched a “scaling program” — a referral program, so that users could invite their friends to the service.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=0cb0c0899bbd" width="1" height="1" alt=""><hr><p><a href="https://medium.com/history-of-vercel/history-of-vercel-cloudup-0cb0c0899bbd">History of Vercel (4/7). 2013. Cloudup. Drag. Drop. Stream.</a> was originally published in <a href="https://medium.com/history-of-vercel">History of Vercel</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
    </channel>
</rss>