In this tutorial I will show you some of the basics of Marko.js and getting it set up with Vite.js!
Why Marko
Marko.js is a JS framework that is SSR (server-side rendered) by default with an MPA (multi-page app) architecture. eBay runs on it. It has been around for a few years but has gotten a lot of updates lately, like the new Vite plugin. Additionally new features that are coming soon like a concise syntax and improved performance will make Marko.js a great choice for many JS devs.
Did I mention even Dan Abramov (of the React core team) said we're on track to go where no JS framework has gone before?
MPA + SSR -- By Default!
Marko's MPA architecture allows it to run without needing a router like React Router or Vue Router, making things that much simpler for devs. And because it is SSR by default, there is no need to worry about anything like Next.js or Gatsby.
In this tutorial I will show how this all works.
Why Vite.js
Vite, like Webpack, takes care of your bundling needs, putting all of your HTML, CSS, JS and in our case .marko
files together to serve to the browser.
Unlike Webpack, Vite is WAY faster and has an extremely minimal configuration. In this case we'll just use the command line and we won't have to worry about any configuration!
Vite also gives us hot module reloading, so when you save, the page automatically reloads. This is a very nice feature because you don't have to restart your dev server when you make changes to your code, it just reloads itself for you.
Let's Go!
First, you need NPX installed. If you haven't done that yet, go here and follow the instructions to install NVM, which will then allow you to get Node.js and NPX on your device.
Here is the repo for the finished code if you don't want to go through the tutorial step-by-step.
We'll call our app's directory marko-vite. Let's run npx @marko/create marko-vite
in the command line. Then arrow down to Example from marko-js/examples
, hit enter, then arrow to vite-express
and hit enter again.
A directory named marko-vite will be generated for us.
Open it up in your code editor, and let's nuke some things.
Delete the components, pages, and services directories.
Next, make a new pages directory and put a file called index.js into it, with the following code:
import template from "./template.marko";
export default (req, res) => {
res.marko(template, {});
};
This just tells our server when it comes to this page, to load in our Marko template.
Next, let's make a basic Marko page!
Make a new file in the src/pages/ directory and call it template.marko
, and add the following code:
<!DOCTYPE html>
<html>
<head>
<title>Marko + Vite</title>
</head>
<body>
<h1>Hello World!</h1>
<div>
<a href="/goodbye">Goodbye!</a>
</div>
<div>
<a href="/counters">Count!</a>
</div>
</body>
</html>
WAIT! Doesn't that look just like HTML? Marko is a superset of HTML, so anything that is HTML can be taken care of by Marko.
Next, let's make a file named goodbye.js in the src/pages directory and put the following code in:
import goodbye from "./goodbye.marko";
export default (req, res) => {
res.marko(goodbye, {});
};
and another file called goodbye.marko:
<!DOCTYPE html>
<html>
<head>
<title>Marko + Vite</title>
</head>
<body>
<h1>See you later world!</h1>
<div>
<div>
Bye Bye!
</div>
👋
</div>
<div>
<a href="/">Hello Again!</a>
</div>
</body>
</html>
Then let's update the src/index.js file to make sure we have the right routes. We'll worry about the /counters
in a little bit:
import { Router } from "express";
import indexPage from "./pages/index";
import goodbyePage from "./pages/goodbye";
export default Router()
.get("/", indexPage)
.get("/goodbye", goodbyePage)
Running the project
Now let's run the project! run npm run dev
and navigate to localhost:3000
. You should see something like this:
and then if you navigate to the goodbye link, you should see something like this:
But wait! We haven't added JS to the browser yet! Let's keep rolling!
Adding Components!
let's make a file in src/pages/
called counters.js
and add the code:
import counters from "./counters.marko";
export default (req, res) => {
res.marko(counters, {});
};
then another file in src/pages/
called counters.marko
and add the code:
<!DOCTYPE html>
<html>
<head>
<title>Marko + Vite</title>
</head>
<body>
<h1>Count 'em up!</h1>
<div>
<counter/>
<counter/>
<counter/>
<counter/>
<counter/>
</div>
<div>
<a href="/">Back to home</a>
</div>
</body>
</html>
Woah! This looks different... where are those counter tags coming from? That's no longer valid HTML! You see, Marko automatically detects Marko components in our src/
directory, and then adds them into their respective places. Pretty nifty!
Adding Counters
Let's make a new directory under src
called components
and then make the file src/components/counter.marko
. Then let's add the code:
class {
onCreate() {
this.state = {
count: 0
};
}
increment() {
this.state.count++;
}
}
<div>
Count is: <output>${state.count}</output>
</div>
<div>
<button on-click('increment')>
Click Here!
</button>
</div>
Here we have a basic counter, which increments based on the on-click handler we added to the button. Marko allows us to combine the JS and the HTML in one page in this way!
(There are cases where you can or should separate out the JS from the .marko file -> see Marko docs for more info).
Now we're almost ready to show off our counters! We just need to add the new <a/>
tag into our template.marko
file and add the routes into our src/index.js
file.
Now our src/index.js
file will look like this:
import { Router } from "express";
import indexPage from "./pages/index";
import goodbyePage from "./pages/goodbye";
import countersPage from "./pages/counters";
export default Router()
.get("/", indexPage)
.get("/goodbye", goodbyePage)
.get("/counters", countersPage);
and our src/pages/template.marko
like this:
<!DOCTYPE html>
<html>
<head>
<title>Marko + Vite</title>
</head>
<body>
<h1>Hello World!</h1>
<div>
<a href="/goodbye">Goodbye!</a>
</div>
<div>
<a href="/counters">Count!</a>
</div>
</body>
</html>
Notice how on saving the files, Vite re-runs things for us - that is hot module reloading at work. Then from our base page, clicking on the count!
link gives us a lot of buttons to click on! Alright!!
Conclusion
Here we can see that using Marko with Vite allows us to use some shortcuts compared to React. SSR by default, no router needed.
Read more about the future of Marko!
See Ryan Carniato's posts FLUURT overview for an overview of the future changes to Marko; UI language for more specifics on the conciseness of Marko's future syntax; and Michael Rawling's Maybe you don't need that SPA about how Marko's server-native architecture puts Marko on the path to sub-component level hydration, enabling high levels of SSR performance.
Top comments (7)
Thanks for the quick tutorial! I'm running into an issue with creating the starter app
npx @marko/create marko-vite
Any ideas?
Wow impressive! Exactly what I need, I will test it, thanks :)
You're welcome Nicolas! Also feel free to join the Marko Discord server if you want to learn more about Marko!
discord.com/invite/marko
Thanks, I'm in Marko's Discord (Nico64) ;)
In the meantime, I had the opportunity to test Marko, the SSR etc. It's validated, I'm using it for the redesign/refacto of a SaaS!
Thank you
Great article!
Thanks Bruno!