Fast abstract ↬

On this article, we’ll be constructing an interactive fiction expertise wherein a person can insert phrases that match elements of speech given by the content material creator. Let’s go!

Creating an interactive expertise with fiction could be a chore with conventional content material administration instruments. Writing the prose, creating the varieties, combining them within the frontend — these are sometimes the area of three completely different folks.

Let’s make it the area of only one content material creator wherein the person will fill out a type earlier than studying the story — creating odd and sometimes humorous tales. One of these expertise was popularized as “Madlibs.”

The madlib creator gives the tip person a type to fill out with no information of how their phrases might be used within the story. After the shape is crammed out, it populates the suitable spots within the story to (hopefully) humorous outcomes. (Giant preview)

How The Generator Will Work

An editor can create a collection of madlibs that an end-user can fill out and save a replica with their distinctive solutions. The editor might be working with the Sanity Studio inside a rich-text subject that we’ll craft to supply extra info for our front-end to construct out varieties.

For the editor, it would really feel like writing commonplace paragraph content material. They’ll be capable of write like they’re used to writing. They’ll then create particular blocks inside their content material that can specify part of speech and show textual content.

The front-end of the appliance can then use that knowledge to each show the textual content and construct a type. We’ll use 11ty to create the frontend with some small templates. The shape that’s constructed will show to the person earlier than they see the textual content. They’ll know what sort of speech and normal context for the phrases and phrases they’ll enter.

After the shape is submitted, they’ll be given their absolutely shaped story (with hopefully hilarious outcomes). This creation will solely be set inside their browser. In the event that they want to share it, they’ll then click on the “Save” button. This can submit the whole textual content to a serverless perform in Netlify to put it aside to the Sanity knowledge retailer. As soon as that has been created, a hyperlink will seem for the person to view the everlasting model of their madlib and share it with pals.

Since 11ty is a static web site generator, we will’t rely on a web site rebuild to generate every person’s saved Madlib on the fly. We are able to use 11ty’s new Serverless mode to construct them on request utilizing Netlify’s On-Demand Builders to cache every Madlib.

The circulation of knowledge via the generator. (Giant preview)

Sanity.io

Sanity.io is a unified content material platform that believes that content material is knowledge and knowledge can be utilized as content material. Sanity pairs a real-time knowledge retailer with three open-source instruments: a robust question language (GROQ), a CMS (Sanity Studio), and a rich-text knowledge specification (Moveable Textual content).

Moveable Textual content

Moveable Textual content is an open-source specification designed to deal with wealthy textual content as knowledge. We’ll be utilizing Moveable Textual content for the wealthy textual content that our editors will enter right into a Sanity Studio. Information will enhance the wealthy textual content in a method that we will create a type on the fly primarily based on the content material.

11ty And 11ty Serverless

11ty is a static web site generator inbuilt Node. It permits builders to ingest knowledge from a number of sources, write templates in a number of templating engines, and output easy, clear HTML.

Within the upcoming 1.0 launch, 11ty is introducing the idea of 11ty Serverless. This replace permits websites to make use of the identical templates and knowledge to render pages by way of a serverless perform or on-demand builder. 11ty Serverless begins to blur the road between “static web site generator” and server-rendered web page.

Netlify On-Demand Builders

Netlify has had serverless features as a part of its platform for years. For instance, an “On-Demand Builder” is a serverless perform devoted to serving a cached file. Every builder works equally to an ordinary serverless perform on the primary name. Netlify then caches that web page on its edge CDN for every extra name.

Extra after leap! Proceed studying under ↓

Constructing The Enhancing Interface And Datastore

Earlier than we will dive into serverless features and the frontend, it might be useful to have our knowledge arrange and able to question.

To do that, we’ll arrange a brand new challenge and set up Sanity’s Studio (an open-source content material platform for managing knowledge in your Sanity Content material Lake).

The ultimate view for an editor within the Sanity Studio. (Giant preview)

To create a brand new challenge, we will use Sanity’s CLI instruments.

First, we have to create a brand new challenge listing to accommodate each the front-end and the studio. I’ve referred to as mine madlibs.

From inside this listing within the command line, run the next instructions:

npm i -g @sanity/cli
sanity init

The sanity init command will run you thru a collection of questions. Title your challenge madlibs, create a brand new dataset referred to as manufacturing, set the “output path” to studio, and for “challenge template,” choose “Clear challenge with no predefined schemas.”

The CLI creates a brand new Sanity challenge and installs all of the wanted dependencies for a brand new studio. Contained in the newly created studio listing, we’ve the whole lot we have to make our modifying expertise.

Earlier than we create the primary interface, run sanity begin within the studio listing to run the studio.

Creating The madlib Schema

A set of schema defines the studio’s modifying interface. To create a brand new interface, we’ll create a brand new schema within the schema folder.

// madlibs/studio/schemas/madlib.js

export default {
  // Title within the knowledge
  title: 'madlib',
  // Title seen to editors
  title: 'Madlib Template',
  // Sort of schema (at this stage both doc or object)
  sort: 'doc',
  // An array of fields
  fields: [
    {
      name: 'title',
      title: 'Title',
      type: 'string'
    },
    {
      title: 'Slug',
      name: 'slug',
      type: 'slug',
      options: {
        source: 'title',
        maxLength: 200, // // will be ignored if slugify is set
      }
    },
  ]
}

The schema file is a JavaScript file that exports an object. This object defines the info’s title, title, sort, and any fields the doc could have.

On this case, we’ll begin with a title string and a slug that may be generated from the title subject. As soon as the file and preliminary code are created, we have to add this schema to our schema.js file.

// /madlibs/studio/schema/schema.js

// First, we should import the schema creator
import createSchema from 'half:@sanity/base/schema-creator'

// Then import schema sorts from any plugins that may expose them
import schemaTypes from 'all:half:@sanity/base/schema-type'

// Imports our new schema
import madlib from './madlib'

// Then we give our schema to the builder and supply the consequence to Sanity
export default createSchema({
  // We title our schema
  title: 'default',
  // Then proceed to concatenate our doc sort
  // to those supplied by any plugins which are put in
  sorts: schemaTypes.concat([
    // document
    // adds the schema to the list the studio will display
    madlib,
  ])
})

Subsequent, we have to create a wealthy textual content editor for our madlib authors to put in writing the templates. Sanity has a built-in method of dealing with wealthy textual content that may convert to the versatile Moveable Textual content knowledge construction.

To create the editor, we use an array subject that comprises a particular schema sort: block.

The block sort will return all of the default choices for wealthy textual content. We are able to additionally lengthen this sort to create specialty blocks for our editors.

export default {
  // Title within the knowledge
  title: 'madlib',
  // Title seen to editors
  title: 'Madlib Template',
  // Sort of schema (at this stage both doc or object)
  sort: 'doc',
  // An array of fields
  fields: [
    {
      name: 'title',
      title: 'Title',
      type: 'string'
    },
    {
      title: 'Slug',
      name: 'slug',
      type: 'slug',
      options: {
        source: 'title',
        maxLength: 200, // // will be ignored if slugify is set
      }
    },
    {
      title: 'Madlib Text',
      name: 'text',
      type: 'array',
      of: [
        {
          type: 'block',
          name: 'block',
          of: [
            // A new type of field that we'll create next
            { type: 'madlibField' }
          ]
        },
      ]
    },
  ]
}

This code will arrange the Moveable Textual content editor. It builds numerous sorts of “blocks.” Blocks roughly equate to top-level knowledge within the JSON knowledge that Moveable Textual content will return. By default, commonplace blocks take the form of issues like paragraphs, headers, lists, and so on.

Customized blocks will be created for issues like pictures, movies, and different knowledge. For our madlib fields, we wish to make “inline” blocks — blocks that circulation inside one among these bigger blocks. To do this, the block sort can settle for its personal of array. These fields will be any sort, however we’ll make a customized sort and add it to our schema in our case.

Creating A Customized Schema Sort For The Madlib Discipline

To create a brand new customized sort, we have to create a brand new file and import the schema into schema.js as we did for a brand new doc sort.

As an alternative of making a schema with a sort of doc, we have to create one among sort: object.

This practice sort must have two fields: the show textual content and the grammar sort. By structuring the info this fashion, we open up future prospects for inspecting our content material.

Alongside the info fields for this sort, we will additionally specify a customized preview to point out a couple of subject displayed within the wealthy textual content. To make this work, we outline a React element that can settle for the info from the fields and show the textual content the way in which we would like it.

// /madlibs/studio/schemas/object/madLibField.js
import React from 'react'

// A React Element that takes hte worth of knowledge
// and returns a easy preview of the info that can be utilized
// within the wealthy textual content editor
perform madlibPreview({ worth }) {
  const { textual content, grammar } = worth

  return (
    
      {textual content} ({grammar})
    
  );
}

export default {
  title: 'Madlib Discipline Particulars',
  title: 'madlibField',
  sort: 'object',
  fields: [
    {
      name: 'displayText',
      title: 'Display Text',
      type: 'string'
    },
    {
      name: 'grammar',
      title: 'Grammar Type',
      type: 'string'
    }
  ],
  // Defines a preview for the info within the Wealthy Textual content editor
  preview: {
    choose: {
      // Selects knowledge to move to our element
      textual content: 'displayText',
      grammar: 'grammar'
    },
    
    // Tells the sector which preview to make use of
    element: madlibPreview,
  },
}

As soon as that’s created, we will add it to our schemas array and use it as a kind in our Moveable Textual content blocks.

// /madlibs/studio/schemas/schema.js
// First, we should import the schema creator
import createSchema from 'half:@sanity/base/schema-creator'

// Then import schema sorts from any plugins that may expose them
import schemaTypes from 'all:half:@sanity/base/schema-type'

import madlib from './madlib'
// Import the brand new object
import madlibField from './objects/madlibField'

// Then we give our schema to the builder and supply the consequence to Sanity
export default createSchema({
  // We title our schema
  title: 'default',
  // Then proceed to concatenate our doc sort
  // to those supplied by any plugins which are put in
  sorts: schemaTypes.concat([
    // documents
    madlib,
    //objects
    madlibField
  ])
})

Creating The Schema For Consumer-generated Madlibs

Because the user-generated madlibs might be submitted from our frontend, we don’t technically want a schema for them. Nonetheless, if we create a schema, we get a simple technique to see all of the entries (and delete them if crucial).

We would like the construction for these paperwork to be the identical as our madlib templates. The principle variations on this schema from our madlib schema are the title, title, and, optionally, making the fields read-only.

// /madlibs/studio/schema/userLib.js
export default {
  title: 'userLib',
  title: 'Consumer Generated Madlibs',
  sort: 'doc',
  fields: [
    {
      name: 'title',
      title: 'Title',
      type: 'string',
      readOnly: true
    },
    {
      title: 'Slug',
      name: 'slug',
      type: 'slug',
      readOnly: true,
      options: {
        source: 'title',
        maxLength: 200, // // will be ignored if slugify is set
      },
    },
    {
      title: 'Madlib Text',
      name: 'text',
      type: 'array',
      readOnly: true,
      of: [
        {
          type: 'block',
          name: 'block',
          of: [
            { type: 'madlibField' }
          ]
        },
      ]
    },
  ]
}

With that, we will add it to our schema.js file, and our admin is full. Earlier than we transfer on, you should definitely add no less than one madlib template. I discovered the primary paragraph of Moby Dick labored surprisingly effectively for some humorous outcomes.

Constructing The Frontend With 11ty

To create the frontend, we’ll use 11ty. 11ty is a static web site generator written in and prolonged by Node. It does the job of making HTML from a number of sources of knowledge effectively, and with some new options, we will lengthen that to server-rendered pages and build-rendered pages.

Setting Up 11ty

First, we’ll must get issues arrange.

Inside the principle madlibs listing, let’s create a brand new web site listing. This listing will home our 11ty web site.

Open a brand new terminal and alter the listing into the web site listing. From there, we have to set up a couple of dependencies.

// Create a brand new bundle.json
npm init -y
// Set up 11ty and Sanity utilities
npm set up @11ty/[email protected] @sanity/block-content-to-html @sanity/consumer

As soon as these have been put in, we’ll add a few scripts to our bundle.json

// /madlibs/web site/bundle.json

"scripts": {
 "begin": "eleventy --serve",
 "construct": "eleventy"
  },

Now that we’ve a construct and begin script, let’s add a base template for our pages to make use of and an index web page.

By default, 11ty will look in an _includes listing for our templates, so create that listing and add a base.njk file to it.

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta title="viewport" content material="width=device-width, initial-scale=1.0">
  <title>Madlibs</title>
  {# Primary reset #}
  <hyperlink rel="stylesheet" href="https://unpkg.com/some-nice-basic-css/international.css" />

</head>

<physique>
  <nav class="container navigation">
    <a category="brand" href="https://smashingmagazine.com/">Madlibs</a>
  </nav>

  <div class="stack container bordered">
    {# Inserts content material from a web page file and renders it as html #}
    { secure }
  </div>

  {% block scripts %}
  {# Block to insert scripts from little one templates #}
  {% endblock %}
</physique>

</html>

As soon as we’ve a template, we will create a web page. First, within the root of the web site listing, add an index.html file. Subsequent, we’ll use frontmatter so as to add a little bit knowledge — a title and the structure file to make use of.

---
title: Madlibs 
structure: 'base.njk'
---
<p>Some madlibs to take your thoughts off issues. They're saved in <a href="https://sanity.io">Sanity.io</a>, constructed with <a href="https://11ty.dev">11ty</a>, and do fascinating issues with Netlify serverless features.</p>

Now you can begin 11ty by working npm begin within the web site listing.

Now, we wish to create pages dynamically from knowledge from Sanity. To do that, we’ll create a JavaScript Information file and a Pagination template.

Earlier than we dive into these information, we have to create a few utilities for working with the Sanity knowledge.

Contained in the web site listing, let’s create a utils listing.

The primary utility we’d like is an initialized Sanity JS consumer. First, create a file named sanityClient.js within the new utils listing.

// /madlibs/web site/utils/sanityClient.js'
const sanityClient = require('@sanity/consumer')
module.exports = sanityClient({
  // The challenge ID
  projectId: '<YOUR-ID>',
  // The dataset we created
  dataset: 'manufacturing',
  // The API model we wish to use
  // Greatest follow is to set this to at present's date
  apiVersion: '2021-06-07',
  // Use the CDN as a substitute of fetching straight from the info retailer
  useCdn: true
})

Since our wealthy textual content is saved as Moveable Textual content JSON, we’d like a technique to convert the info to HTML. We’ll create a utility to do that for us. First, create a file named portableTextUtils.js within the utils listing.

For Sanity and 11ty websites, we usually will wish to convert the JSON to both Markdown or HTML. For this web site, we’ll use HTML to have granular management over the output.

Earlier, we put in @sanity/block-content-to-html, which can assist us serialize the info to HTML. The bundle will work on all primary sorts of Moveable Textual content blocks and kinds. Nonetheless, we’ve a customized block sort that wants a customized serializer.

// Initializes the bundle
const toHtml = require('@sanity/block-content-to-html')
const h = toHtml.h;

const serializers = {
  sorts: {
    madlibField: ({ node }) => {
      // Takes every node of `sort` `madlibField`
      // and returns an HTML span with an id, class, and textual content
      return h('span', node.displayText, { id: node._key, className: 'empty' })
    }
  }
}

const prepText = (knowledge) => {
  // Takes the info from a particular Sanity doc
  // and creates a brand new htmlText property to include the HTML
  // This lets us maintain the Moveable Textual content knowledge intact and nonetheless show HTML
  return {
    ...knowledge,
    htmlText: toHtml({
      blocks: knowledge.textual content, // Moveable Textual content knowledge
      serializers: serializers // The serializer to make use of
    })
  }
}

// We solely must export prepText for our features
module.exports = { prepText }

The serializers object on this code has a sorts object. On this object, we create a specialised serializer for any sort. The important thing within the object ought to match the sort given in our knowledge. In our case, that is madlibField. Every sort could have a perform that returns a component written utilizing hyperscript features.

On this case, we create a span with youngsters of the displayText from the present knowledge. Later we’ll want distinctive IDs primarily based on the info’s _key, and we’ll want a category to fashion these. We offer these in an object because the third argument for the h() perform. We’ll use this identical serializer setup for each our madlib templates and the user-generated madlibs.

Now that we’ve our utilities, it’s time to create a JavaScript knowledge file. First, create a _data within the web site listing. On this file, we will add international knowledge to our 11ty web site. Subsequent, create a madlibs.js file. This file is the place our JavaScript will run to drag every madlib template. The info might be accessible to any of our templates and pages beneath the madlibs key.

// Get our utilities
const consumer = require('../utils/sanityClient')
const {prepText} = require('../utils/portableTextUtils')
// The GROQ question used to seek out particular paperwork and 
// form the output 
const question = `*[_type == "madlib"]{
    title,
    "slug": slug.present,
    textual content,
    _id,
    "formFields": textual content[]{
        youngsters[_type == "madlibField"]{
            displayText,
            grammar,
            _key
      }
      }.youngsters[]
  }`

module.exports = async perform() {
    // Fetch knowledge primarily based on the question
    const madlibs = await consumer.fetch(question);

    // Put together the Moveable Textual content knowledge
    const preppedMadlib = madlibs.map(prepText)
    // Return the total array
    return preppedMadlib
}

To fetch the info, we have to get the utilities we simply created. The Sanity consumer has a fetch() technique to move a GROQ question. We’ll map over the array of paperwork the question returns to arrange their Moveable Textual content after which return that to 11ty’s knowledge cascade.

The GROQ question on this code instance is doing a lot of the work for us. We begin by requesting all paperwork with a _type of madlib from our Sanity content material lake. Then we specify which knowledge we wish to return. The info begins merely: we’d like the title, slug, wealthy textual content, and id from the doc, however we additionally wish to reformat the info right into a set of type fields, as effectively.

To do this, we create a brand new property on the info being returned: formFields. This seems on the textual content knowledge (a Moveable Textual content array) and loops over it with the [] operator. We are able to then construct a brand new challenge on this knowledge like we’re doing with the whole doc with the {} operator.

Every textual content object has a youngsters array. We are able to loop via that, and if the merchandise matches the filter contained in the [], we will run one other projection on that. On this case, we’re filtering all youngsters which have a _type == "madlibField". In different phrases, any inline block that has an merchandise with the sort we created. We want the displayText, grammar, and _key for every of those. This can return an array of textual content objects with the youngsters matching our filter. We have to flatten this to be an array of kids. To do that, we will add the .youngsters[] after the tasks. This can return a flat array with simply the youngsters components we’d like.

This offers us all of the paperwork in an array with simply the info we’d like (together with newly reformatted objects).

To make use of them in our 11ty construct, we’d like a template that can use Pagination.

Within the root of the web site, create a madlib.njk file. This file will generate every madlib web page from the info.

---
structure: 'base.njk'
pagination:
  knowledge: madlibs
  alias: madlib
  dimension: 1
permalink: "madlibs/{ slug }/index.html"
---

Within the entrance matter of this file, we specify some knowledge 11ty can use to generate our pages:

  • structure
    The template to make use of to render the web page.
  • pagination
    An object with pagination info.
  • pagination.knowledge
    The info key for pagination to learn.
  • pagination.alias
    A key to make use of on this file for ease.
  • pagination.dimension
    The variety of madlibs per web page (on this case, 1 per web page to create particular person pages).
  • permalink
    The URLs at which every of those ought to reside (will be partially generated from knowledge).

With that knowledge in place, we will specify find out how to show every bit of knowledge for an merchandise within the array.

---
structure: 'base.njk'
pagination:
  knowledge: madlibs
  alias: madlib
  dimension: 1
permalink: "madlibs/{ slug }/index.html"
---

<h2>{{ madlib.title }}</h2>
<p><em>Directions:</em> Fill out this way, submit it and get your story. It can hopfully make little-to-no sense. Afterward, it can save you the madlib and ship it to your mates.</p>
<div class="madlibtext">
<a href="https://smashingmagazine.com/2021/10/static-first-madlib-generator-portable-text-netlify-builder-functions/#" class="saver">Reserve it</a>
{ secure }
</div>
<h2>Kind</h2>
<type class="madlibForm stack">
{% for enter in madlib.formFields %}
    <label>
        {{ enter.displayText }} ({{ enter.grammar }})
        <enter sort="textual content" class="libInput" title={{enter._key}}>
    </label>
{% endfor %}
<button>Carried out</button>
</type>

We are able to correctly format the title and HTML textual content. We are able to then use the formFields array to create a type that customers can enter their distinctive solutions.

There’s some extra markup to be used in our JavaScript — a type button and a hyperlink to save lots of the finalized madlib. The hyperlink and madlib textual content might be hidden (no peeking for our customers!).

For each madlib template, you created in your studio, 11ty will construct a singular web page. The ultimate URLs ought to appear to be this

http://localhost:8080/madlibs/the-slug-in-the-studio/

Making The Madlibs Interactive

With our madlibs generated, we have to make them interactive. We’ll sprinkle a little bit JavaScript and CSS to make them interactive. Earlier than we will use CSS and JS, we have to inform 11ty to repeat the static information to our constructed web site.

Copying Static Property To The Closing Construct

Within the root of the web site listing, create the next information and directories:

  • belongings/css/fashion.css — for any extra styling,
  • belongings/js/madlib.js — for the interactions,
  • .eleventy.js — the 11ty configuration file.

When these information are created, we have to inform 11ty to repeat the belongings to the ultimate construct. These directions reside within the .eleventy.js configuration file.

module.exports = perform(eleventyConfig) {
 eleventyConfig.addPassthroughCopy("belongings/");
}

This instructs 11ty to repeat the whole belongings listing to the ultimate construct.

The one crucial CSS to make the positioning work is a snippet to cover and present the madlib textual content. Nonetheless, in order for you the entire feel and look, you will discover all of the kinds on this file.

.madlibtext {
 show: none
}
.madlibtext.present {
 show: block;
}

Filling In The Madlib With Consumer Enter And JavaScript

Any frontend framework will work with 11ty in case you arrange a construct course of. For this instance, we’ll use plain JavaScript to maintain issues easy. The primary process is to take the person knowledge within the type and populate the generic madlib template that 11ty generated from our Sanity knowledge.

// Connect the shape handler
const type = doc.querySelector('.madlibForm')
type.addEventListener('submit', completeLib);

perform showText() {
  // Discover the madlib textual content within the doc
  const textDiv = doc.querySelector('.madlibtext')
  // Toggle the category "present" to be current
  textDiv.classList.toggle('present')
}

// A perform that takes the submit occasion
// From the occasion, it would get the contents of the inputs
// and write them to web page and present the total textual content
perform completeLib(occasion) {
  // Do not submit the shape
  occasion.preventDefault();
  const { goal } = occasion // The goal is the shape factor

  // Get all inputs from the shape in array format
  const inputs = Array.from(goal.components)

  inputs.forEach(enter => {
    // The button is an enter and we do not need that within the ultimate knowledge
    if (enter.sort != 'textual content') return
    // Discover a span by the enter's title
    // These will each be the _key worth
    const replacedContent = doc.getElementById(enter.title)
    // Substitute the content material of the span with the enter's worth
    replacedContent.innerHTML = enter.worth
  })
  // Present the finished madlib
  showText();
}

This performance is available in three elements: attaching an occasion listener, taking the shape enter, inserting it into the HTML, after which exhibiting the textual content.

When the shape is submitted, the code creates an array from the shape’s inputs. Subsequent, it finds components on the web page with ids that match the enter’s title — each created from the _key values of every block. It then replaces the content material of that factor with the worth from the info.

As soon as that’s finished, we toggle the total madlib textual content to point out on the web page.

We have to add this script to the web page. To do that, we create a brand new template for the madlibs to make use of. Within the _includes listing, create a file named lib.njk. This template will lengthen the bottom template we created and insert the script on the backside of the web page’s physique.

{% extends 'base.njk' %}

{% block scripts %}
<script>
  var pt = { secure }
  var knowledge = {
      libId: `{{ madlib._id }}`,
      libTitle: `{{ madlib.title }}`
  }
</script>
<script src="https://smashingmagazine.com/belongings/js/madlib.js"></script>
{% endblock %}

Then, our madlib.njk pagination template wants to make use of this new template for its structure.

---
structure: 'lib.njk'
pagination:
  knowledge: madlibs
  alias: madlib
  dimension: 1
permalink: "madlibs/{ slug }/index.html"
---

// web page content material

We now have a functioning madlib generator. To make this extra sturdy, let’s enable customers to save lots of and share their accomplished madlibs.

Saving A Consumer Madlib To Sanity With A Netlify Perform

Now that we’ve a madlib exhibited to the person, we have to create the hyperlink for saving ship the knowledge to Sanity.

To do this, we’ll add some extra performance to our front-end JavaScript. However, first, we have to add some extra knowledge pulled from Sanity into our JavaScript, so we’ll add a few new variables within the scripts block on the lib.njk template.

{% extends 'base.njk' %}

{% block scripts %}
<script>
  // Moveable Textual content knowledge
  var pt = { secure }
  var knowledge = {
      libId: `{{ madlib._id }}`,
      libTitle: `{{ madlib.title }}`
  }
</script>
<script src="https://smashingmagazine.com/belongings/js/madlib.js"></script>
{% endblock %}

We are able to write a script to ship it and the user-generated solutions to a serverless perform to ship to Sanity with that extra knowledge.

// /madlibs/web site/belongings/js/madlib.js

// ... completeLib()

async perform saveLib(occasion) {
  occasion.preventDefault();

  // Return an Map of ids and content material to show into an object
  const blocks = Array.from(doc.querySelectorAll('.empty')).map(merchandise => {
    return [item.id, { content: item.outerText }]
  })
  // Creates Object prepared for storage from blocks map
  const userContentBlocks = Object.fromEntries(blocks);

  // Codecs the info for posting
  const finalData = {
    userContentBlocks,
    pt, // From nunjucks on web page
    ...knowledge // From nunjucks on web page
  }

  // Runs the put up knowledge perform for createLib
  postData('/.netlify/features/createLib', finalData)
    .then(knowledge => {
      // When put up is profitable
      // Create a div for the ultimate hyperlink
      const landingZone = doc.createElement('div')
      // Give the hyperlink a category
      landingZone.className = "libUrl"
      // Add the div after the saving hyperlink
      saver.after(landingZone)
      // Add the brand new hyperlink contained in the touchdown zone
      landingZone.innerHTML = `Your url is /userlibs/${knowledge._id}/`

    }).catch(error => {
      // When errors occur, do one thing with them
      console.log(error)
    });
}

async perform postData(url="", knowledge = {}) {
  // A wrapper perform for traditional JS fetch
  const response = await fetch(url, {
    technique: 'POST',
    mode: 'cors',
    cache: 'no-cache',
    credentials: 'same-origin',
    headers: {
      'Content material-Sort': 'utility/json'
    },
    physique: JSON.stringify(knowledge)
  });
  return response.json(); // parses JSON response into native JavaScript objects
}

We add a brand new occasion listener to the “Save” hyperlink in our HTML.

The saveLib perform will take the info from the web page and the user-generated knowledge and mix them in an object to be dealt with by a brand new serverless perform. The serverless perform must take that knowledge and create a brand new Sanity doc. When creating the perform, we would like it to return the _id for the brand new doc. We use that to create a singular hyperlink that we add to the web page. This hyperlink might be the place the newly generated web page might be.

Setting Up Netlify Dev

To make use of Netlify Features, we’ll must get our challenge arrange on Netlify. We would like Netlify to construct and serve from the web site listing. To offer Netlify this info, we have to create a netlify.toml file on the root of the whole challenge.

[build]
 command = "npm run construct" # Command to run
 features = "features"            # Listing we retailer the features
 publish = "_site"                        # Folder to publish (11ty mechanically makes the _site folder
 base = "web site"                                # Folder that's the root of the construct

To develop these regionally, it’s useful to put in Netlify’s CLI globally.

npm set up -g netlify-cli

As soon as that’s put in, you possibly can run netlify dev in your challenge. This can take the place of working your begin NPM script.

The CLI will run you thru connecting your repository to Netlify. As soon as it’s finished, we’re able to develop our first perform.

Creating A Perform To Save Madlibs To Sanity

Since our TOML file units the features listing to features, we have to create the listing. Contained in the listing, make a createLib.js file. This would be the serverless perform for making a madlib within the Sanity knowledge retailer.

The usual Sanity consumer we’ve been utilizing is read-only. To offer it write permissions, we have to reconfigure it to make use of an API learn+write token. To generate a token, log into the challenge dashboard and go to the challenge settings on your madlibs challenge. Within the settings, discover the Tokens space and generate a brand new token with “Editor” permissions. When the token is generated, save the string to Netlify’s atmosphere variables dashboard with the title SANITY_TOKEN. Netlify Dev will mechanically pull these atmosphere variables into the challenge whereas working.

To reconfigure the consumer, we’ll require the file from our utilities, after which run the .config() technique. This can allow us to set any configuration worth for this particular use. We’ll set the token to the brand new atmosphere variable and set useCdn to false.

// Sanity JS Consumer
// The construct consumer is read-only
// To make use of to put in writing, we have to add an API token with correct permissions
const consumer = require('../utils/sanityClient')
consumer.config({
    token: course of.env.SANITY_TOKEN,
    useCdn: false
})

The essential construction for a Netlify perform is to export a handler perform that’s handed an occasion and returns an object with a standing code and string physique.

// Grabs native env variables from .env file
// Not crucial if utilizing Netlify Dev CLI
require('dotenv').config()

// Sanity JS Consumer
// The construct consumer is read-only
// To make use of to put in writing, we have to add an API token with correct permissions
const consumer = require('../utils/sanityClient')
consumer.config({
  token: course of.env.SANITY_TOKEN,
  useCdn: false
})

// Small ID creation bundle
const { nanoid } = require('nanoid')

exports.handler = async (occasion) => {
  // Get knowledge off the occasion physique
  const {
    pt,
    userContentBlocks,
    id,
    libTitle
  } = JSON.parse(occasion.physique)

  // Create new Moveable Textual content JSON
  // from the previous PT and the person submissions
  const newBlocks = findAndReplace(pt, userContentBlocks)

  // Create new Sanity doc object
  // The doc's _id and slug are primarily based on a singular ID from nanoid
  const docId = nanoid()
  const doc = {
    _type: "userLib",
    _id: docId,
    slug: { present: docId },
    madlib: id,
    title: `${libTitle} creation`,
    textual content: newBlocks,
  }

  // Submit the brand new doc object to Sanity
  // Return the response again to the browser
  return consumer.create(doc).then((res) => {
    // Log the success into our perform log
    console.log(`Userlib was created, doc ID is ${res._id}`)
    // return with a 200 standing and a stringified JSON object we get from the Sanity API
    return { statusCode: 200, physique: JSON.stringify(doc) };
  }).catch(err => {
    // If there's an error, log it
    // and return a 500 error and a JSON string of the error
    console.log(err)
    return {
      statusCode: 500, physique: JSON.stringify(err)
    }
  })
}

// Perform for modifying the Moveable Textual content JSON
// pt is the unique transportable Textual content
// mods is an object of modifications to make 
perform findAndReplace(pt, mods) {
  // For every block object, examine to see if a mod is required and return an object
  const newPT = pt.map((block) => ({
    ...block, // Insert all present knowledge
    youngsters: block.youngsters.map(span => {
      // For each merchandise in youngsters, see if there is a modification on the mods object
      // If there may be, set modContent to the brand new content material, if not, set it to the unique textual content 
      const modContent = mods[span._key] ? mods[span._key].content material : span.textual content
      // Return an object with all the unique knowledge, and a brand new property
      // displayText to be used within the frontends
      return {
        ...span,
        displayText: modContent
      }
    })
  }))
  // Return the brand new Moveable Textual content JSON
  return newPT
}

The physique is the info we simply submitted. For ease, we’ll destructure the info off the occasion.physique object. Then, we have to evaluate the unique Moveable Textual content and the person content material we submitted and create the brand new Moveable Textual content JSON that we will undergo Sanity.

To do this, we run a discover and change perform. This perform maps over the unique Moveable Textual content and for each little one within the blocks, change its content material with the corresponding knowledge from the modfications object. If there isn’t a modification, it would retailer the unique textual content.

With modified Moveable Textual content in hand, we will create a brand new object to retailer as a doc within the Sanity content material lake. Every doc wants a singular identifier (which we will use the nanoid NPM bundle to create. We’ll additionally let this newly created ID be the slug for consistency.

The remainder of the info is mapped to the correct key in our userLib schema we created within the studio and submitted with the authenticated consumer’s .create() technique. When success or failure returns from Sanity, we move that alongside to the frontend for dealing with.

Now, we’ve knowledge being saved to our Sanity challenge. Go forward and fill out a madlib and submit. You’ll be able to view the creation within the studio. These hyperlinks that we’re producing don’t work but, although. That is the place 11ty Serverless is available in.

Setting Up 11ty Serverless

You could have seen after we put in 11ty that we used a particular model. That is the beta of the upcoming 1.0 launch. 11ty Serverless is likely one of the large new options in that launch.

Putting in The Serverless Plugin

11ty Serverless is an included plugin that may be initialized to create all of the boilerplate for working 11ty in a serverless perform. To stand up and working, we have to add the plugin to our .eleventy.js configuration file.

const { EleventyServerlessBundlerPlugin } = require("@11ty/eleventy");

module.exports = perform (eleventyConfig) {
  eleventyConfig.addPassthroughCopy("belongings/");

  eleventyConfig.addPlugin(EleventyServerlessBundlerPlugin, {
    title: "userlibs", // the title to make use of for the features
    functionsDir: "./features/", // The features listing
    copy: ["utils/"], // Any information that must be copied to make our scripts work
    excludeDependencies: ["./_data/madlibs.js"] // Exclude any information you do not wish to run
  });
};

After creating this file, restart 11ty by rerunning netlify dev. On the following run, 11ty will create a brand new listing in our features listing named userlibs (matching the title within the serverless configuration) to accommodate the whole lot it must should run in a serverless perform. The index.js file on this listing is created if it doesn’t exist, however any adjustments you make will persist.

We have to make one small change to the tip of this file. By default, 11ty Serverless will initialize utilizing commonplace serverless features. This can run the perform on each load of the route. That’s an costly load for content material that may’t be modified after it’s been generated. As an alternative, we will change it to make use of Netlify’s On-Demand Builders. This can construct the web page on the primary request and cache the consequence for any later requests. This cache will persist till the following construct of the positioning.

To replace the perform, open the index.js file and alter the ending of the file.

// Remark this line out
exports.handler = handler

// Uncomment these traces
const { builder } = require("@netlify/features");
exports.handler = builder(handler);

Since this file is utilizing Netlify’s features bundle, we additionally want to put in that bundle.

npm set up @netlify/features

Creating A Information File For Consumer-generated Madlibs

Now that we’ve an On-Demand Builder, we have to pull the info for user-generated madlibs. We are able to create a brand new JavaScript knowledge file within the _data file named userlibs.js. Like our madlibs knowledge file, the file title would be the key to get this knowledge in our templates.

// /madlibs/web site/_data/userlibs.js

const consumer = require('../utils/sanityClient')
const {prepText} = require('../utils/portableTextUtils')

const question = `*[_type == "userLib"]{
    title,
    "slug": slug.present,
    textual content,
    _id
  }`

module.exports = async perform() {
    const madlibs = await consumer.fetch(question);
    // Shield in opposition to no madlibs returning
    if (madlibs.size == 0) return {"404": {}} 

    // Run via our transportable textual content serializer
    const preppedMadlib = madlibs.map(prepText)

    // Convert the array of paperwork into an object
    // Every merchandise within the Object could have a key of the merchandise slug
    // 11ty's Pagination will create pages for every one
    const mapLibs = preppedMadlib.map(merchandise => ([item.slug, item]))
    const objLibs = Object.fromEntries(mapLibs)
    return objLibs
}

This knowledge file is much like what we wrote earlier, however as a substitute of returning the array, we have to return an object. The article’s keys are what the serverless bundle will use to drag the proper madlib on request. In our case, we’ll make the merchandise’s slug the important thing because the serverless route might be searching for a slug.

Now that the plugin is prepared, we will create a brand new pagination template to make use of the generated perform.

Within the root of our web site, add a userlibs.njk template. This template might be just like the madlibs.njk template, however it would use completely different knowledge with none interactivity.

---
structure: 'base.njk'
pagination:
  knowledge: userLibs
  alias: userlib
  dimension: 1
  serverless: eleventy.serverless.path.slug

permalink: 
  userlibs: "/userlibs/:slug/"
---

<h2>{{ userlib.title }}</h2>
<div>
  { secure }
</div>

On this template, we use base.njk to keep away from together with the JavaScript. We specify the brand new userlibs knowledge for pagination.

To drag the proper knowledge, we have to specify what the lookup key might be. On the pagination object, we do that with the serverless property. When utilizing serverless routes, we get entry to a brand new object: eleventy.serverless. On this object, there’s a path object that comprises info on what URL the person requested. On this case, we’ll have a slug property on that object. That should correspond to a key on our pagination knowledge.

To get the slug on our path, we have to add it to the permalink object. 11ty Serverless permits for a couple of route for a template. The route’s key must match the title supplied within the .eleventy.js configuration. On this case, it ought to be userlibs. We specify the static /userlibs/ begin to the trail after which add a dynamic factor: :slug/. This slug might be what will get handed to eleventy.serverless.path.slug.

Now, the hyperlink that we created earlier by submitting a madlib to Sanity will work.

Subsequent Steps

Now we’ve a madlib generator that saves to an information retailer. We construct solely the mandatory pages to permit a person to create a brand new madlib. Once they create one, we make these pages on-demand with 11ty and Netlify Features. From right here, we will lengthen this additional.

  • Statically construct the user-generated content material in addition to render them on request.
  • Create a counter for the entire variety of madlibs saved by every madlib template.
  • Create a listing of phrases customers use by elements of speech.

When you possibly can statically construct AND dynamically render, what kinds of functions does this open up?

Smashing Editorial
(vf, yk, il)

#Constructing #StaticFirst #MadLib #Generator #Moveable #Textual content #Netlify #OnDemand #Builder #Features #Smashing #Journal

Leave a Reply

Your email address will not be published. Required fields are marked *