⮌ back | Overview | Home

🧹 The Linter

The <Linter /> component helps you to avoid common issues when using view transitions.

Contents

Why should I use this component?

View transitions can fail or show strange effects if you set transition attributes the wrong way. These situations are often not easy to debug. The <Linter> component helps you to ensure the following rules when using view transitions.

Example output

The <Linter/> component writes its results to the browser console while you navigate your pages.

Example Linter output Example Linter output

Unique view transition names

Unique view transitions names are a requirement of the view transition API. Chrome will display an error message in the console - even without the linter - if this rule is violated. But at least as of version 120, Chrome only reports violations for the page you are navigating away from, not the one you are navigating to. The <Linter> component always reports both sides of a navigation.

Where do view transition names come from?

When looking for view transition names, the linter considers these sources:

Nested elements

Nested elements that define transition:persist or data-vtbot-replace can lead to strange results and non-deterministic behavior. It is best to avoid them.

Identifiers for transition:persist

In Astro, transition:persist can be used without an explicit identifier. Then the name of the view transition element is used, which is fine if it was set by transition:name=.... If neither transition:name nor an identifier for transition:persist is specified, Astro implicitly uses the identifier of the transition area, which looks like this: astro-ya3eq23s-4. Often this matches the desired element on the target page, but it is also quite fragile. It is best practice to use explicit names. So if you use transition:persist with an implicit, generated identifier, the <Linter> component will issue a warning.

Loss of transition names and scoped styles

Astro emits scoped styles by default, with different strategies to bind styles to elements. What they all have in common is that they use special identifiers for CSS classes (astro-xxxxxxxx) or custom data attributes (data-astro-cid-xxxxxxxxxx). If a DOM element gets copied to another DOM with transition:persist or data-vtbot-replace , these identifiers are copied together with the element. This typically results in the link to the style sheet being broken if the style sheet to which the identifier refers is not also copied. If you want to use the same style sheets on different pages, you can define global styles with the is:global directive, for example.

The way Astro links elements with their view-transition-name uses a very similar mechanism. Elements that are copied to another DOM often lose their view-transition-name if the name is defined with transition:name. Alternatively, you can define the name of the view transition with the style attribute: <elem style="view-transition-name: identifier">. In this way, it remains a part of the persisted element.

The <Linter> component will issue a warning in these situations. They are probably errors, but the <Linter> does not know if the effects are intended.

You can make the linter keep its mouth shut for false positives by adding a meta element that lists transition names that should be ignored to the <head> of your page:

1
<meta name="vtbot-linter-ignore" content="zoom1 zoom2 zoom3 swing1 swing2 swing3" />

Valid identifiers for view-transition-names

When you use transition:name or transition:persist, Astro normalizes the specified identifier to ensure that it is a valid CSS <custom-ident> value. But it is also possible to define view-transition-namesdirectly in style attributes or style sheets. In this case, the <Linter> component will issue a warning if the identifier is not valid.

Valid custom identifiers must not start with a number, i.e. a digit, or a “-” sign followed by a digit. Characters beside letters, digits, underscore and the minus-sign must be properly escaped. The reserved names unset, initial, inherit and none are also invalid identifiers for view-transition-names.

Script types not handled by the router

<script> elements might have a type attribute. The most common value for this attribute is "module" to indicate that the script is an ESM. Other valid values are "" and text/javascript. Both indicate that this script is JavaScript. As this is the default anyways, authors are encouraged to not use these values.

If the type attribute is set to anything else, the script is ignored by the browser. This is fine for e.g. "text/partytown" where the partytown lib searches and executes such scripts. Some optimization techniques that instrument HTML might also use there own values. Even though the script will be executed eventually, this typically happens after the astro:page-load event. If you define an event listener for astro:page-load in those scripts, it will not be called. The <Linter> component will issue a warning in these situations.

Usage

Install astro-vtbot in your project as an Astro integration with npx astro add astro-vtbot or as a node package with npm install astro-vtbot. If you choose the integration option, vtbot will automatically add the <Linter/> component to all the pages that use <ViewTransitions>.

If you do not choose the integration, you have to add the <Linter/> component to your pages manually: Insert the <Linter/> component near the <ViewTransitions/> component into the <head> of your page.

Layout.astro
1
---
2
import ViewTransitions from 'astro:transitions';
3
import Linter from 'astro-vtbot/components/Linter.astro';
4
---
5
<html>
6
<head>
7
<ViewTransitions />
8
<Linter />
9
...
10
</head>
11
<body>
12
...
13
</body>
14
</html>

Properties

The <Linter/> component offers two properties to control its behavior:

PropertyTypeEffect
productionbooleanActivate the Linter in production mode. Typically, the linter is active in DEV mode, only. Set this property if you want to activate the Linter in production, too.
expandedbooleanIn the log that is written to the browsers console, the top level groups are collapsed by default. With expand you can force them open from the start.
YourDevelopmentPage.astro
1
---
2
import { ViewTransitions } from 'astro:transitions'
3
import { BrakePad } from 'astro-vtbot/components/BrakePad.astro'
4
---
5
<html>
6
<head>
7
<ViewTransitions />
8
<Linter expanded production>
9
...
10
</head>
11
</html>