Elder.js is an opinionated static site generator and web framework for Svelte built with SEO in mind. Elder supports Server Side Rendering and Static Site Generation. It is the result of work to build ElderGuide.com and was purpose built to solve the unique challenges of building flagship SEO sites with 10-100k+ pages.
Elder Guide Co-Founder Nick Reese has worked on five major SEO properties over the past 14 years. After leading the transition of several complex sites to static site generators he loved the benefits of the Jamstack, but wished there was a better solution for complex, data intensive, projects.
As of September 2020, the ElderGuide.com team expects to maintain the project until at least 2023-2024. For a clearer vision of what is considered "in scope" and what isn't, see this comment.
Getting Started
We're going to use degit to download the Elder.js template. Here is a demo of the template.
Clone Template
I will name my project ajcwebdev-elder
. Feel free to give your project a unique name, but make sure to use your own project's name for any terminal commands.
npx degit Elderjs/template ajcwebdev-elder
This will create the starter project.
Install Dependencies
cd ajcwebdev-elder
npm install
This will install dependencies from npm.
Create a GitHub repo
Give it the same name as your project.
git init
git add . && git commit -m "Initial commit"
git branch -M main
git remote add origin https://github.com/ajcwebdev/ajcwebdev-elder.git
git push -u origin main
Start Project
npm start
This will start the development server. Navigate to http://localhost:3000
.
If everything worked you should see the default home page. There is a brief explanation of the starting template and instructions for setting up your development environment.
Developing using the Template:
As the page says, it's recommended that you run two separate terminals for development. One is for the server and the other is for watching your changes with Rollup so you don't have to restart your server on every change.
Terminal 1: Server
npm run dev:server
This starts the dev server.
Terminal 2: Rollup
npm run dev:rollup
This tells Rollup to watch your project.
Here is our project structure:
That's a lot of stuff. First we'll just look for our home page. Go to the src
folder.
Inside the src
folder is a routes
folder with a home
folder containing Home.svelte
.
Svelte components are a superset of HTML and can have up to three main sections. All three sections are optional:
script
style
markup
script
<script>
import HookDetail from '../../components/HookDetail.svelte';
import BlogTeaser from '../../components/BlogTeaser.svelte';
import Clock from '../../components/Clock.svelte';
export let data, helpers;
export let foo;
const hooks = data.hookInterface.map((hook) => (
{...hook, link: helpers.permalinks.hooks({ slug: hook.hook })}
));
</script>
Inside the script
tags we setup hooks
which we'll talk about in future parts and we import three components:
HookDetail
BlogTeaser
Clock
style
<style>
.banner {
padding: 1rem 2rem;
background: #eee;
border-radius: 2rem;
margin-bottom: 1rem;
}
.entries {
display: grid;
grid-template-columns: 1fr;
margin: 3rem 0;
}
@media (min-width: 768px) {
.entries {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
margin: 3rem 0;
}
:global(.entries .entry) {
margin-right: 1rem;
}
}
:global(.entry) {
padding: 1rem;
border: 1px solid #ddd;
border-radius: 1rem;
margin-bottom: 1rem;
background: white;
}
.about {
margin-bottom: 2rem;
}
@media (min-width: 768px) {
.hydrate {
display: grid;
grid-template-columns: 80% 20%;
}
}
.hooks {
display: grid;
grid-template-columns: 100%;
}
@media (min-width: 768px) {
.hooks {
grid-template-columns: 50% 50%;
}
}
</style>
We have a default style that we'll leave alone.
markup
svelte:head
is used to set the title
of the page.
<svelte:head>
<title>Elder.js Template: Home</title>
</svelte:head>
Below svelte:head
is a conditional that runs a test to make sure the hooks are working.
{#if data.testingHooks}
<div class="banner">
<p>Testing hooks worked</p>
{#if data.cpus}
<p>If you use Elder.js to build your site, it will span all {data.cpus.length} cpus listed below:</p>
<ol>
{#each data.cpus as cpu}
<li>{cpu.model}</li>
{/each}
</ol>
{/if}
</div>
{/if}
The banner provides links to the docs and GitHub.
<div class="banner">
<h1>Hello World: Welcome to Elder.js!</h1>
<p>Woot! You've got Elder.js installed. This template is designed to show you the ropes of how things work.</p>
<p>We've tried to make this site a bit of a tutorial, but be sure to check out the full <a href="https://elderguide.com/tech/elderjs/">Elder.js docs.</a></p>
<p>Enjoy playing around with Elder.js and, if you hit a snag with the template, open a <a href="https://github.com/Elderjs/template/issues">GitHub issue.</a></p>
</div>
The BlogTeaser
component displays a list of blog posts with a title and summary.
<div class="blog">
<div class="entries">
{#each data.markdown.blog as blog}
<BlogTeaser {blog} {helpers} />
{/each}
</div>
</div>
The about
section explains the different routes we will explore throughout the series.
<div class="about">
<h2>About This Template</h2>
<p>This example project is made up of 4 routes; you can find them by looking within the <span class="code">./src/routes/</span> folder. They are:</p>
<ul>
<li><a href={helpers.permalinks.simple({ slug: 'simple' })}>Simple</a> - A barebones route.</li>
<li>Home - The page you are on.</li>
<li>Blog - Linked from above, but you can also see a blog post by checking out: <a href={helpers.permalinks.blog({ slug: 'getting-started' })}>'Getting Started'</a>.</li>
<li>Hooks - These are how you customize Elder.js. Details are below, and we've used the <span class="code">hookInterface</span> to build out dedicated pages for each hook as well.</li>
</ul>
<p>The goal in showing off these 3 routes is to give you enough of an example to see how a site is built with Elder.js (but one that isn't too complex to overwhelm you).</p>
The development environment section explains how to use the dev server and Rollup for local development.
<h3>Development Environment:</h3>
<p>If you ran <span class="code">npm start</span> to see this page, we recommend you stop that command. Instead, open two terminals and run:</p>
<ol>
<li><span class="code">npm run dev:server</span> - This uses nodemon to restart the development server when files change.</li>
<li><span class="code">npm run dev:rollup</span> - This uses Rollup to recompile your Svelte templates as you change them.</li>
</ol>
<p><strong>Note:</strong> Make sure you also check out <code>npm run build</code>, which will statically generate this same site so that it can be deployed with a static site host such as Netlify, Cloudflare Workers, Vercel, or S3.</p>
</div>
The hydrate
section uses the Clock
component to display a working clock.
<div class="hydrate">
<div class="left">
<h2>Partial Hydration:</h2>
<p>Svelte.js shines at bringing interactivity to otherwise static websites.</p>
<p>By default, Elder.js statically renders Svelte components, only mounting them in the browser when it encounters a Svelte component which includes the <code>hydrate-client={JSON.stringify({})}</code> prop. Note: the actual prop is 'hydrate-client={}' but Svelte doesn't render empty objects</p>
<p>The <a href="https://svelte.dev/examples#clock">clock</a> on this page is an example of a component that has been hydrated on the client.</p>
<p>This approach makes it easy to build SEO friendly websites, with Svelte for interactivity when needed.</p>
<p>By default all hydrated components are lazy loaded with an intersection observer.</p>
</div>
<div class="right">
<Clock hydrate-client={{}} />
</div>
</div>
The final section explains hooks.
<h2>The Hook Interface</h2>
<p>Once you've digested the guides above and understand how to handle client hydration, we encourage you to explore the hook interface below.</p>
<p>Hooks are the primary way to customize Elder.js and the list below outlines exactly what each hook can be used for.</p>
The HookDetail
component displays a list of hooks.
<div class="hooks">
{#each hooks as hook, i}
<HookDetail {hook} {i} hookEntityDefinitions={data.hookEntityDefinitions} />
{/each}
</div>
To make sure we have everything set up correctly we'll change the title
and the banner
.
<svelte:head>
<title>ajcwebdev-elder</title>
</svelte:head>
<div class="banner">
<h1>ajcwebdev</h1>
<p>Woot!</p>
</div>
Return to your browser to see the change.
Discussion (2)
Thanks for writing this!
Quick tip: put code in backticks rather than screenshoting your code editor. For the sake of accessibility and copy/pasting
Hey Felix, yeah I'm in the process of switching all of the code in my articles to gists. I originally was using screenshots because I find that with a lot of these frameworks the syntax highlighting just doesn't work very well, but gists seem to get the job done for the most part.