# Tags VS Class API
## TL;DR
* Developer Experience
* Fewer concepts to learn and less boilerplate
* Encourages safer and more correct patterns
* Optimized for colocation, refactoring and componentization
* User Experience
* Only interactive code is bundled for the browser
* Server work is not re-executed on the browser
* Performant fine-grained updates instead of vdom/diffing
## Examples
### Sync input.title to document.title
<div class="comparison">
```jsx
/* tags */
<const/{ title } = input/>
<effect() {
document.title = title;
}/>
```
```jsx
/* class */
class {
onMount() {
this.setTitle();
}
onUpdate() {
this.setTitle();
}
setTitle() {
if (this.previousTitle !== this.input.title) {
document.title = this.previousTitle = this.input.title;
}
}
}
```
</div>
The tags api here is less code, but also less likely to introduce a bug.
It's easy for developers to forget to guard updates in the class api, leading to unecessary work or bugs.
### Sync input.title to document.title (with cleanup)
<div class="comparison">
```jsx
/* tags */
<const/{ title } = input/>
<effect() {
const originalTitle = document.title;
document.title = title;
$signal.onabort = () => {
document.title = originalTitle;
}
}/>
```
```jsx
/* class */
class {
onMount() {
this.originalTitle = document.title;
this.syncTitle();
}
onUpdate() {
this.syncTitle();
}
onDestroy() {
document.title = this.originalTitle;
}
syncTitle() {
const { title } = this.input;
if (title !== this.previousTitle) {
document.title = this.previousTitle = title;
}
}
}
```
</div>
It's also easier in the tags api to colocate the cleanup functionality (when a component is destroyed) with the rest of the feature code.
### Use input to open and close a native dialog
<div class="comparison">
```jsx
/* tags */
<const/{ open } = input/>
<effect() {
if (open) {
el().showModal();
} else {
el().close();
}
}/>
<dialog/el>
<p>Greetings, one and all!</p>
</dialog>
```
```jsx
/* class */
class {
onMount() {
this.syncOpen();
}
onUpdate() {
this.syncOpen();
}
syncOpen() {
const { open } = this.input;
if (open !== this.previousOpen) {
this.previousOpen = open;
if (open) {
this.getEl("el").showModal();
} else {
this.getEl("el").close();
}
}
}
}
<dialog key="el">
<p>Greetings, one and all!</p>
</dialog>
```
</div>
In the class api with this example we spread things across multiple lifecycle events which can lead to problems when other code is in those lifecycle events. So now lets create a component that merges this dialog logic _and_ the title logic from earlier.
### Combined Examples
<div class="comparison">
```jsx
/* tags */
// Title functionality
<const/{ title } = input/>
<effect() {
const originalTitle = document.title;
document.title = title;
$signal.onabort = () => {
document.title = originalTitle;
}
}/>
// Dialog functionality
<const/{ open } = input/>
<effect() {
if (open) {
el().showModal();
} else {
el().close();
}
}/>
<dialog/el>
<p>Greetings, one and all!</p>
</dialog>
```
```jsx
/* class */
class {
onMount() {
// Title functionality
this.originalTitle = document.title;
this.syncTitle();
// Dialog functionality
this.syncOpen();
}
onUpdate() {
// Title functionality
this.syncTitle();
// Dialog functionality
this.syncOpen();
}
onDestroy() {
// Title functionality
document.title = this.originalTitle;
}
// Dialog functionality
syncOpen() {
const { open } = this.input;
if (open !== this.previousOpen) {
this.previousOpen = open;
if (open) {
this.getEl("el").showModal();
} else {
this.getEl("el").close();
}
}
}
// Title functionality
syncTitle() {
if (this.previousTitle !== this.input.title) {
document.title = this.previousTitle = this.input.title;
}
}
}
// Dialog functionality
<dialog key="el">
<p>Greetings, one and all!</p>
</dialog>
```
</div>
The dialog and title related features are interspersed in the class api lifecycle methods. This makes it hard to tell where the code for either feature is and a challenge to refactor out those features later.
With the tags API you can cut and paste either feature into a separate Marko file without having to move around any code.
## Take Away
The tags api may look different from the class api, but it's mostly about moving "state" into variables exposed from tags. It allows colocating state/lifecycle logic with your markup which makes code easier to write, read, reason-about and refactor.
...And it means the compiler can better understand your code and deliver optimized experiences to the user.
<style>
.comparison {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(25rem, 1fr));
gap: 1rem;
pre {
margin: 0;
}
}
</style>