← Home

Getting Started with Elder.js

By Nick Reese

Sweet! So you've got the Elder.js starter template up and running. What's next?

4 Routes To Explore

This project is structured to follow the required Elder.js folder structure which you can see below, but in short you've got several routes in the ./src/routes/ folder.

Each of these routes are designed to showcase something different.

Now that you've got Elder.js up and running let's talk about some customization options.

Elder.js Community Discord

Getting started with Elder.js and want to connect with other users? Join us over at the Elder.js channel within the official Svelte discord.

SEO Auditing

As you play with this template you'll notice that there is some SEO auditing going on.

Under the hood Elder.js uses @elderjs/plugin-seo-check to check each page generated for more than 50 common search engine optimization (SEO) issues.

If for some reason these warnings are to verbose you can adjust the level by editing your ./elder.config.js to be:

'@elderjs/plugin-seo-check': {
  display: ['errors'], // If the errors are too verbose remove 'warnings'

This will silence some of the more opinionated rules.

How to Customize Elder.js


Currently this template is running two plugins:

If you are looking for other plugins check out these:

If you want to share an idea for a plugin or want to help develop an Elder.js plugin, check out Plugin Ideas discussion on the Elder.js repo.

Hooks: Customize and Control Elder.js:

Once you've explored the templates above, it is worth looking a bit at how the hooks work.

Open up the ./src/hooks.js file and look at the hooks this project uses.

You'll see there are a few hooks in there.

If you uncomment the hook with the name of compressHtml and reload this page, you'll see that the html is now compressed... but the code blocks are broken. (they always say don't compress html with regex!).

In plain english, this hook takes the htmlString, modifies it (compresses it), and returns it.

Now that you see the power of hooks, let's have you add your first hook which illustrates how you'd add analytics code to every page of your site.

Copy and paste the hook below into your hooks.js file.

    hook: 'stacks',
    name: 'addAnalyticstoFooter',
    description: 'Add analytics to Footer.',
    priority: 1, // we want it to be last
    run: async ({ footerStack }) => {
        source: 'hooks',
        string: `<!-- your analytics code here -->`,
        priority: 1,
      return { footerStack }

If you reload your html, you should see the html comment from the hook.

In this hook we are manipulating a "stack."

Under the hood, Elder.js uses stacks to predictably manage in what order strings are rendered.

In this hook we're just adding our analytics code at a priority of 1 (last).

If stacks seem foreign, just remember they are a list of strings with some meta data.

Hooks In Depth:

Elder.js runs it's hooks system based on it's 'hookInterface'. This interface defines which hooks can do what and what properties they have.

In building Elder.js we found that if anything can be mutated at anytime, a system quickly gets hard to reason about.

The 'hookInterface' is designed to solve that problem. While you can explore all of the hooks on the homepage, before you go try adding a malicious hook that is designed to corrupt important data during page load.

Add the hook below to your hooks.js file and reload this page:

 hook: 'data',
 name: 'maliciousHook',
 description: 'Can we break anything?',
 priority: 1, // this will be called last
 run: async ({ helpers, data, settings, request, query }) => {
   settings = null;
   request = null;
   helpers = null;
   query = null;

   return { settings, request, query, helpers }

On reload, if you check the console you'll see that this hook wasn't able to mutate any of the props due to the way the hookInterface is configured.

Essentially only properties that are able to be mutated on a hook, will be mutated on the hook. This helps keep plugins and developers honest and makes maintaining the project in the future easier to reason about. :)

If you're interested in exploring hooks more check out the full Elder.js documentation on ElderGuide.

A Brief Look At Shortcodes

Shortcodes are a great way to customize otherwise static content. They are especially useful when using a CMS or external content store. The most common use cases include:

  1. You need a placeholder for dynamic content that isn't available when the static content is written.
  2. You want a future proof way of adding 'design flair' to your site.
  3. When you need a dynamic data point that changes often and don't want to go back and update it each time it changes.

Here is an example of their power:

This site has 19 pages on it.

If you add another page to this site, you'll see that the number of pages above adjusts accordingly. This dynamic ability is powered via a shortcode which you can see in ./src/shortcodes.js.

Usually this sort of customization takes a ton of preprocessing, parsing, etc, but Elder.js handles it all for you. Simply define a shortcode and a function that returns what you want it to be replaced with and Elder.js will handle the rest.

Learning Opportunities:

  1. Try using the 'box' shortcode to see how to add design flair.
  2. Think about how you could use a shortcode to fetch data from an external API and how that would add major flexibility to your static content.
  3. Try adding a "Clock" Svelte component to this page. (Details in the ./src/shortcodes.js)

Out of the Box Usecases

  1. Pulling in your latest Tweets or replies to one of your tweets.
  2. You run your own ad platform for your site. You can use a shortcode that hits an external API allowing you render your ads on the server.
  3. You want to embed arbitrary JS on the page (event tracking or something) but only when a shortcode is present. (totally doable)
  4. You need to add ld+json to your head for a specific page, but don't have it wired into the template. You could use a shortcode to do so.

Elder.js Project Structure

Under the hood Elder.js does quite a bit of magic based on the file structure below but more importantly the rollup.config.js is setup to match this file structure. Since Rollup handles all of the bundling of our Svelte components, we recommend you follow this structure unless you like tinkering with bundlers.

Project Root
| elder.config.js
| package.json
| rollup.config.js
| ... (other common stuff, .gitignore, svelte.config.js... etc)
| -- src
| -- | -- build.js
| -- | -- server.js
| -- | -- hooks.js
| -- | -- shortcodes.js
| -- helpers
| -- | -- index.js
| -- | -- ...
| -- layouts
| -- | -- Layout.svelte
| -- routes
| -- | -- [route] ('blog' in this example)
| -- | -- | -- Blog.svelte
| -- | -- | -- route.js
| -- plugins
| -- | -- [plugin] ('elderjs-plugin-your-plugin' for example)
| -- | -- | -- index.js
| -- components
| -- | -- [component] ('Contact' in this example)
| -- | -- | -- Contact.svelte

On this Project:
| -- assets
| -- | -- items to be copied to the 'distDir' defined in your 'elder.config.js'. See hooks.js.

Deploying Elder.js

If you are looking to deploy your statically generated Elder.js site Cloudflare Pages has a great guide.

Customizing The CSS:

For this template any css in the ./src/layouts/Layout.svelte will be made available on all pages. You can also import CSS like we do with the css file at ./assets/style.css and that will get added to the CSS file Elder.js generates.

SSR and Dynamic Experiences:

Elder.js started as a static site generator but today it is used in production as an SSR framework as well.

For more information look at the route found in ./src/routes/ssr/. In short, the req and next functions from express or polka (used in this template) are made available there. This means you'll have access to sessions and anything else you'd need to make logged in or otherwise dynamic experiences.

For even more control make sure to checkout the middleware hook.

Copying of Assets

Another hook that you'll see is one that copies anything in your ./assets/ to the distDir defined in your elder.config.js (which is./public/ folder by default in this project).