Skip to content

Commit cab8417

Browse files
committed
chore: first draft
1 parent b0b61a7 commit cab8417

File tree

25 files changed

+249
-39
lines changed

25 files changed

+249
-39
lines changed

packages/docs/src/routes/(blog)/blog/(articles)/astro-qwik/index.mdx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
---
22
title: 'Astro + Qwik: Houston, we have Resumability!'
3-
authorName: 'Jack Shelton'
3+
authors:
4+
- Jack Shelton
45
tags: ['Qwik']
56
date: 'November 8, 2023'
67
canonical: 'https://www.builder.io/blog/astro-qwik'

packages/docs/src/routes/(blog)/blog/(articles)/framer-motion-qwik/index.mdx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
---
22
title: 'Building Framer Motion Animations Inside a Qwik Application'
3-
authorName: 'Yoav Ganbar'
3+
authors:
4+
- 'Yoav Ganbar'
45
tags: ['Web development']
56
date: 'March 21, 2023'
67
canonical: 'https://www.builder.io/blog/framer-motion-qwik'

packages/docs/src/routes/(blog)/blog/(articles)/introducing-qwik-starters/index.mdx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
---
22
title: 'Introducing Qwik starters - get up and running with Qwik now'
3-
authorName: 'Miško Hevery'
3+
authors:
4+
- 'Miško Hevery'
45
tags: ['Qwik']
56
date: 'December 14, 2021'
67
canonical: 'https://www.builder.io/blog/introducing-qwik-starters'

packages/docs/src/routes/(blog)/blog/(articles)/module-extraction-the-silent-web-revolution/index.mdx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
---
22
title: 'Code Extraction: The Silent Web Revolution'
3-
authorName: 'Manu Mtz.-Almeida'
3+
authors:
4+
- 'Manu Mtz.-Almeida'
45
tags: ['Qwik']
56
date: 'February 21, 2023'
67
canonical: 'https://www.builder.io/blog/module-extraction-the-silent-web-revolution'
Loading
Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
---
2+
title: 'Qwik 1.14.0: Introducing the Preloader'
3+
authors:
4+
- 'Wout Mertens'
5+
- 'Maïeul Chevalier'
6+
- 'Shai Reznik'
7+
tags: ['Web development']
8+
date: 'April 23, 2025'
9+
canonical: 'https://qwik.dev/blog/qwik-1-14-preloader'
10+
---
11+
12+
import { ArticleBlock } from '~/routes/(blog)/blog/components/mdx/article-block';
13+
import { DiscordLink } from '~/routes/(blog)/blog/components/mdx/discord-link';
14+
import { Image } from 'qwik-image';
15+
import CodeSandbox from '~/components/code-sandbox/index.tsx';
16+
import serviceWorkerDelayDemo from './service-worker-delay-demo-crop.webm';
17+
import modulepreloadNoDelayDemo from './modulepreload-no-delay-demo-crop.webm';
18+
import serviceWorkerVsModulepreloadIllustration from './service-worker-vs-modulepreload-illustration.webp';
19+
import serviceWorkerRegistrationPenalty from './service-worker-registration-penalty.webp';
20+
21+
<ArticleBlock>
22+
23+
We’re super excited to announce the release of Qwik 1.14.0, our most fundamental update to Qwik since the release of 1.0.0!
24+
25+
Here is how you can download it:
26+
```json
27+
"@builder.io/qwik": "~1.14.0",
28+
"@builder.io/qwik-city": "~1.14.0",
29+
"eslint-plugin-qwik": "~1.14.0",
30+
```
31+
32+
## A Qwik recap
33+
34+
One of the core features that makes Qwik so… well.. quick… is what we call “**Javascript Streaming**” – the unique ability to execute a part of the code as soon as it is ready, before all the code has been downloaded – speeding up your website’s [TTI (Time To Interactive)]([https://developer.mozilla.org/en-US/docs/Glossary/Time_to_interactive](https://developer.mozilla.org/en-US/docs/Glossary/Time_to_interactive)) similarly to how [video streaming]([https://www.cloudflare.com/learning/video/what-is-buffering/](https://www.cloudflare.com/learning/video/what-is-buffering/)) is significantly faster than downloading an entire video and then playing it thanks to buffering and on demand delivery.
35+
36+
This mechanism is first enabled at build-time thanks to the [Qwik optimizer](https://qwik.dev/docs/advanced/optimizer/#optimizer), which looks for $ signs and splits your application code into smaller pieces (called “segments”). Then the bundler ([Rollup](https://rollupjs.org/)) groups these segments into small javascript files (called “bundles”) that hold related segments together.
37+
38+
The goal is to have bundles neither too big nor small, where each corresponds to an interactive part of the UI.
39+
40+
Then at runtime, the framework can leverage a service-worker to prefetch and cache all the bundles available on the page so that when a user interacts with a certain component, the code for it is already buffered and can be “lazy-executed” **right away** (unlike “lazy loaded” on demand).
41+
42+
If the user interacts but the network is really slow and therefore prefetching is still going on, Qwik will prioritize downloading those bundles and will execute them as soon as they’re available.
43+
44+
In comparison, the other frameworks need to execute at least all of the visible UI code to attach the event listeners, and therefore they must download all of that code before executing it in a process called “hydration”.
45+
46+
In effect, this means that TTI on Qwik apps can often be as fast as ~5s on a slow 3G network connection, even for very complex applications. With hydration on the other hand, TTI often reaches ~20s on slow 3G on simple websites and 60s or more on more complex applications as it increases proportionally to the amount of components that need to hydrate.
47+
48+
### A long standing “Buffering” issue
49+
50+
In Qwik 1.11.0 and 1.13.0, we fixed some long lasting bugs that were leading to “under-prefetching” and “over-prefetching under certain conditions. With those fixes, we made sure that all and only the bundles required for the user interactions available on a page are prefetched, therefore preventing any sort of network delays.
51+
52+
But there were still 2 somewhat hidden issues we only uncovered recently through manual testing on a used up low-end smartphone. It turns out there is a small cost to serving bundles from the cache through the service worker:
53+
54+
- On old/low-end devices with poor CPUs, the first interactions on Qwik components would lead to small delays, even with a good network connection.
55+
- There was a small startup penalty to register the service worker, preventing any reprioritization of the bundles before the registration.
56+
57+
Thankfully, using a service worker isn’t the only way we can prefetch and cache javascript bundles on the client, and we have found an even better alternative to it.
58+
59+
## What’s new: a better way to “buffer” javascript bundles
60+
61+
In Qwik 1.14, we've transitioned away from using a service worker in favor of a solution leveraging [`<link rel="modulepreload">`](<https://devdocs.io/html/attributes/rel/modulepreload>) as the new default.
62+
63+
The solution consists of a small script called the “Qwik Preloader”. Once it is downloaded on the client, it adds the `<link rel="modulepreload" href=”my-bundle.js#segment123456”>` to the html `<head>`, which tells the browser to preload and buffer the corresponding JavaScript bundles.
64+
65+
By doing so, we not only get rid of any startup penalty, but also make any interaction on Qwik components absolutely instant once the bundles for it have been preloaded, even on devices with poor CPUs 🚀
66+
67+
### No more first interaction delays on old/low-end devices
68+
69+
On our tests on a pretty used up Xiaomi note 7 pro, the delays only reached ~100ms and were barely noticeable to the human eye. This means that only very old or used-up devices should have suffered from this bottleneck, but this is still an area we wanted to improve upon.
70+
71+
<div class="flex flex-col sm:flex-row gap-4 sm:gap-8 justify-center">
72+
<figure>
73+
<video src={serviceWorkerDelayDemo} controls muted class="lg:max-w-md sm:max-w-xs max-w-full" />
74+
<figcaption class="text-center mt-2">Service Worker delay</figcaption>
75+
</figure>
76+
77+
<figure>
78+
<video src={modulepreloadNoDelayDemo} controls muted class="lg:max-w-md sm:max-w-xs max-w-full" />
79+
<figcaption class="text-center mt-2">modulepreload</figcaption>
80+
</figure>
81+
</div>
82+
83+
If you look closely at the two screen recordings above, you will notice that the text is first marked with an underline when focused, and then the Accordion opens the selected item. But the difference is that with the service worker, the first interaction on the Accordion component is a little delayed. With modulepreload, all interactions are instant. (You might want to set the video to 0.5x speed to better see the difference.)
84+
85+
The reason why there is such a delay with the service worker on devices where the CPU is slow is because retrieving a chunk from the CacheControl with the Service Worker doesn't always take 1ms, but sometimes 10ms or more. So for example to retrieve 15 bundles on click, an old device can in total take 150ms or more instead of 15ms.
86+
87+
In contrast, modulepreloaded bundles are downloaded and compiled by the browser. They live in memory and are ready to be served instantly on request. So if all the bundles required for an interaction have been preloaded, the interaction will be instant. There is no network request, no service worker interception, no retrieving of bundles from the CacheStorage.
88+
89+
Here’s a small Excalidraw to illustrate the difference:
90+
91+
<Image src={serviceWorkerVsModulepreloadIllustration} alt="Service Worker vs modulepreload illustration" />
92+
93+
### No more startup penalty
94+
95+
The Service Worker had to be registered before any prefetching could happen. Not only did this negatively affect TTI and [TBT (Total Blocking Time)](https://developer.chrome.com/docs/lighthouse/performance/lighthouse-total-blocking-time#:~:text=Blocking%20Time%20audit-,What%20TBT%20measures,Paint%20and%20Time%20to%20Interactive.) a little, but it also prevented Qwik from reprioritizing bundles if the user interacted before the registration.
96+
<Image src={serviceWorkerRegistrationPenalty} alt="Service Worker registration penalty" />
97+
98+
With modulepreload, we can now start preloading bundles **as soon as the browser is idle**. This means [FCP (First Contentful Paint)](https://web.dev/articles/fcp) and [LCP (Largest Contentful Paint)](https://web.dev/articles/optimize-lcp#:~:text=Largest%20Contentful%20Paint%20(LCP)%20is,is%20rendered%20within%20the%20viewport.) remain as fast as possible, and slightly better TTI and TBT scores because we can prioritize buffering the bundles needed for a user interaction as soon as the page is visible to the user.
99+
### Even better prioritization based on heuristics
100+
101+
Reprioritization is a powerful feature - allowing Qwik to change the “buffering” order based on the user’s actions.
102+
103+
But it’s even better if the framework can already know ahead of time what to “buffer” in which order.
104+
105+
Qwik can now out of the box have better guesses about bundles that are more likely to be needed with other bundles, as they are “related”.
106+
107+
It is not perfect (for that we have [Qwik Insights]( which requires a database), but it’s quite a bit better than before! Qwik now gives “better scores” to more important things like “user events” or “above the fold” components over things it assumes are less important.
108+
109+
For example, Qwik assumes that the search input on the navbar above the fold is more likely to be used sooner than a Button on a footer far down below the fold, but if the user scrolls and clicks the button on the footer, Qwik will re-prioritize its bundles before the search input.
110+
### Faster CI times
111+
112+
Thanks to the performance improvements above, our full CI tests on [qwik.dev](<http://qwik.dev>) now run in about ~10 minutes instead of ~15, and most of it can be attributed to the E2E tests which do many renders in a browser and were therefore bootstrapping a lot of service workers.
113+
## **How do I get all of this Goodness?** (**Migration steps)**
114+
115+
Migrating to Qwik 1.14.0 with the Qwik Preloader as the new default should not require much effort on your end, but there are a few changes you should know about.
116+
117+
Currently 93% of browsers support “modulepreload”, which wasn't the case when we started using the service worker for “buffering”.
118+
119+
For non-supported devices, the preloader will fallback to `<link rel=preload>`, which doesn't compile the code in advance but still works and brings the support to 100%. In effect, you can rest assured all your users will experience lightning speed regardless of their device.
120+
121+
Here are the Steps you need to follow:
122+
123+
### 1. Unregister the service worker
124+
125+
The service worker is no longer used, but to make sure it is being uninstalled from your user’s browser (to prevent it making your app slower) we recommend waiting a few weeks/months before removing the `<ServiceWorkerRegister />` from your root component.
126+
127+
Both the qwik-city service worker and the experimental Qwik prefetch service worker have been updated to do this automatically for you.
128+
129+
So do not remove the service worker registration (in `root.tsx`) from your app just yet. Wait until your users have loaded your site at least once, or long enough for their browser to have cleared the site data.
130+
131+
### 2. IMPORTANT: Configure Your Cache-Control headers
132+
133+
With the service worker no longer forcibly caching bundles, it’s important that you configure appropriate HTTP caching headers using your cloud provider's recommended way of setting them up, for example using vercel.json for Vercel or netlify.toml for Netlify.
134+
135+
The bundles and assets are normally served at `/build` and `/assets`, respectively. Their filenames contain a content-based hash, making them immutable. If they change, their name changes as well. You can therefore set long-lived caching headers for these.
136+
137+
The recommended header is:
138+
139+
```json
140+
Cache-Control: public, max-age=31536000, immutable
141+
```
142+
143+
> 💡 tip: You can re-add the starter for your deployment target with `npx qwik add`, which should update the deployment configuration to use the correct headers.
144+
145+
### 3. Configure translations caching
146+
147+
If your app uses [`compiled-i18n`](https://github.com/wmertens/compiled-i18n) or [`qwik-speak`](https://github.com/robisim74/qwik-speak), translated bundles (`build/[locale]/*.js`) might retain identical filenames across builds, even when translations change. Consider how long you want to cache these files for so users get the latest translations.
148+
149+
Note that this was also a problem with the service worker, and now you have the ability to configure the cache headers for these files, so it's a positive change.
150+
151+
### 4. Enjoy!
152+
153+
We’ve worked countless hours for the past few months, manually testing fixes on simulated and real (old phones) environments in order to pin-point all the issues and make sure Qwik holds true to its promise of delivering the best user experience..
154+
155+
Now that this is fixed, we are shifting our focus back to the upcoming release of Qwik V2.
156+
157+
So stay tuned!
158+
159+
*In the meantime, we'd love your feedback* — let us know how this update improves your apps on our discord <DiscordLink/>.
160+
161+
</ArticleBlock>
162+
163+
164+

packages/docs/src/routes/(blog)/blog/(articles)/qwik-1-2-performance-autopilot/index.mdx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
---
22
title: 'Qwik 1.2: Performance in Autopilot'
3-
authorName: 'Manu Mtz.-Almeida'
3+
authors:
4+
- 'Manu Mtz.-Almeida'
45
tags: ['Web development']
56
date: 'June 29, 2023'
67
canonical: 'https://www.builder.io/blog/qwik-1-2-performance-autopilot'

packages/docs/src/routes/(blog)/blog/(articles)/qwik-2-coming-soon/index.mdx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
---
22
title: 'Towards Qwik 2.0: Lighter, Faster, Better'
3-
authorName: 'The Qwik Team'
3+
authors:
4+
- 'The Qwik Team'
45
tags: ['Qwik']
56
date: 'February 9, 2024'
67
canonical: 'https://www.builder.io/blog/qwik-2-coming-soon'

packages/docs/src/routes/(blog)/blog/(articles)/qwik-and-qwik-city-have-reached-beta/index.mdx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
---
22
title: 'Qwik and Qwik City have reached beta! 🎉'
3-
authorName: 'Miško Hevery'
3+
authors:
4+
- 'Miško Hevery'
45
tags: ['Web development']
56
date: 'September 19, 2022'
67
canonical: 'https://www.builder.io/blog/qwik-and-qwik-city-have-reached-beta'

packages/docs/src/routes/(blog)/blog/(articles)/qwik-city-routing/index.mdx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
---
22
title: 'Qwik City Routing: A Visual Guide'
3-
authorName: 'Vishwas Gopinath'
3+
authors:
4+
- 'Vishwas Gopinath'
45
tags: ['Web development']
56
date: 'July 20, 2023'
67
canonical: 'https://www.builder.io/blog/qwik-city-routing'

packages/docs/src/routes/(blog)/blog/(articles)/qwik-city-server-functions/index.mdx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
---
22
title: 'Introducing Qwik City Server Functions'
3-
authorName: 'Manu Mtz.-Almeida'
3+
authors:
4+
- 'Manu Mtz.-Almeida'
45
tags: ['Qwik']
56
date: 'February 15, 2023'
67
canonical: 'https://www.builder.io/blog/qwik-city-server-functions'

packages/docs/src/routes/(blog)/blog/(articles)/qwik-next-leap/index.mdx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
---
22
title: 'Qwik’s Next Leap - Moving Forward Together'
3-
authorName: 'The Qwik Team'
3+
authors:
4+
- 'Wout Mertens'
5+
- 'Maïeul Chevalier'
6+
- 'Shai Reznik'
47
tags: ['Web Development']
58
date: 'March 26, 2024'
69
canonical: 'https://www.builder.io/blog/qwik-next-leap'

packages/docs/src/routes/(blog)/blog/(articles)/qwik-rc-milestone/index.mdx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
---
22
title: 'Qwik Reaches RC Milestone'
3-
authorName: 'The Qwik Team'
3+
authors:
4+
- 'The Qwik Team'
45
tags: ['Qwik']
56
date: 'March 30, 2023'
67
canonical: 'https://www.builder.io/blog/qwik-rc-milestone'

packages/docs/src/routes/(blog)/blog/(articles)/qwik-tasks/index.mdx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
---
22
title: "Boost Your Site Perf with Qwik's useVisibleTask$ Hook"
3-
authorName: 'Steve Sewell'
3+
authors:
4+
- 'Steve Sewell'
45
tags: ['Web development']
56
date: 'May 9, 2023'
67
canonical: 'https://www.builder.io/blog/qwik-tasks'

packages/docs/src/routes/(blog)/blog/(articles)/qwik-v1/index.mdx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
---
22
title: 'Qwik Reaches v1.0'
3-
authorName: 'The Qwik Team'
3+
authors:
4+
- 'The Qwik Team'
45
tags: ['Qwik']
56
date: 'May 1, 2023'
67
canonical: 'https://www.builder.io/blog/qwik-v1'

packages/docs/src/routes/(blog)/blog/(articles)/resumable-react-how-to-use-react-inside-qwik/index.mdx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
---
22
title: 'Resumable React: How to Use React Inside Qwik'
3-
authorName: 'Yoav Ganbar'
3+
authors:
4+
- 'Yoav Ganbar'
45
tags: ['Web development']
56
date: 'January 30, 2023'
67
canonical: 'https://www.builder.io/blog/resumable-react-how-to-use-react-inside-qwik'

packages/docs/src/routes/(blog)/blog/(articles)/the-qase-for-qwik-love-at-first-tti/index.mdx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
---
22
title: 'The Qase for Qwik: Love At First TTI'
3-
authorName: 'Yoav Ganbar'
3+
authors:
4+
- 'Yoav Ganbar'
45
tags: ['Qwik']
56
date: 'December 19, 2022'
67
canonical: 'https://www.builder.io/blog/the-qase-for-qwik-love-at-first-tti'

packages/docs/src/routes/(blog)/blog/(articles)/type-safe-forms-in-qwik/index.mdx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
---
22
title: 'Type Safe Forms in Qwik with Modular Forms'
3-
authorName: 'Yoav Ganbar'
3+
authors:
4+
- 'Yoav Ganbar'
45
tags: ['Web development']
56
date: 'April 11, 2023'
67
canonical: 'https://www.builder.io/blog/type-safe-forms-in-qwik'

packages/docs/src/routes/(blog)/blog/components/mdx/article-block.tsx

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,22 @@ import { ArticleHero } from './article-hero';
33
import { useDocumentHead, useLocation } from '@builder.io/qwik-city';
44
import { authors, blogArticles } from '~/routes/(blog)/data';
55

6+
import { Image } from 'qwik-image';
7+
68
type Props = { authorLink: string };
79

810
export const ArticleBlock = component$<Props>(({ authorLink }) => {
911
const location = useLocation();
1012
const { frontmatter } = useDocumentHead();
1113
const article = blogArticles.find(({ path }) => path === location.url.pathname);
12-
const author = authors[frontmatter.authorName];
14+
15+
console.log(frontmatter.authors);
16+
const authorLinks = frontmatter.authors.map((author: string) => authors[author].socialLink);
1317

1418
return (
1519
<div class="docs">
16-
<ArticleHero image={article?.image || ''} authorLink={author.socialLink || ''} />
17-
<article class="max-w-[900px] mx-auto tracking-wide">
20+
<ArticleHero image={article?.image || ''} authorLinks={authorLinks} />
21+
<article class="max-w-[900px] mx-auto">
1822
<Slot />
1923
</article>
2024
</div>

packages/docs/src/routes/(blog)/blog/components/mdx/article-hero.tsx

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,19 @@ import { component$ } from '@builder.io/qwik';
22
import { useDocumentHead } from '@builder.io/qwik-city';
33
import { Image } from 'qwik-image';
44

5-
type Props = { image: string; authorLink: string };
5+
type Props = { image: string; authorLinks: string[] };
66

7-
export const ArticleHero = component$<Props>(({ image, authorLink }) => {
7+
export const ArticleHero = component$<Props>(({ image, authorLinks }) => {
88
const { title, frontmatter } = useDocumentHead();
99

10-
if (!frontmatter.authorName || !frontmatter.tags || !frontmatter.date) {
11-
return <>Missing frontmatter props</>;
10+
if (
11+
!frontmatter.authors ||
12+
!Array.isArray(frontmatter.authors) ||
13+
frontmatter.authors.length === 0 ||
14+
!frontmatter.tags ||
15+
!frontmatter.date
16+
) {
17+
return <>Missing frontmatter props or authors array is empty</>;
1218
}
1319

1420
return (
@@ -47,15 +53,21 @@ export const ArticleHero = component$<Props>(({ image, authorLink }) => {
4753
<h4 class="font-semibold uppercase text-center">{frontmatter.date}</h4>
4854
<div class="border border-[color:var(--text-color)] mx-4"></div>
4955
<div class="font-semibold uppercase text-center">
50-
Written By{' '}
51-
<a
52-
class="text-[color:var(--qwik-blue)]"
53-
target="_blank"
54-
rel="noopener"
55-
href={authorLink}
56-
>
57-
{frontmatter.authorName}
58-
</a>
56+
{frontmatter.authors.length > 1 && 'Co-'}Written by{' '}
57+
{frontmatter.authors.map((author: string, index: number) => (
58+
<span key={author}>
59+
<a
60+
class="text-[color:var(--qwik-blue)]"
61+
target="_blank"
62+
rel="noopener"
63+
href={authorLinks[index]}
64+
>
65+
{author}
66+
</a>
67+
{index < frontmatter.authors.length - 1 &&
68+
(index === frontmatter.authors.length - 2 ? ' & ' : ', ')}
69+
</span>
70+
))}
5971
</div>
6072
</div>
6173
</div>

0 commit comments

Comments
 (0)