Categories
Preact

Getting Started with Front End Development with Preact

Preact is a front end web framework that’s similar to React.

It’s smaller and less complex than React.

In this article, we’ll look at how to get started with front end development with Preact.

Getting Started

We can import the Preact module and call render to render the content we want.

For example, we can write:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>app</title>
  </head>
  <body>
    <script type="module">
      import { h, Component, render } from "https://unpkg.com/preact?module";
      const app = h("h1", null, "Hello World");
      render(app, document.body);
    </script>
  </body>
</html>

We call the h method from the preact module to render an element.

The first argument of h is the tag name.

The 2nd argument is the element attributes.

The 3rd argument is the content.

Then we call render with the app and document.body to render app in the body .

Alternatives to JSX

We can also use template strings to render elements.

To do this, we write:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>app</title>
  </head>
  <body>
    <script type="module">
      import { h, Component, render } from "https://unpkg.com/preact?module";
      import htm from "https://unpkg.com/htm?module";
      const html = htm.bind(h);
      function App(props) {
        return html`<h1>Hello ${props.name}!</h1>`;
      }

      render(html`<${App} name="james" />`, document.body);
    </script>
  </body>
</html>

We import the html function and bind the this value in html to the h function.

Then we can use that to render our content with a template string.

We pass in the props to App just like we do with React.

Then we call render with the html tag again with the template string for App .

Preact CLI

We can use the Preact CLI to create a production-ready Preact project.

To install it globally, we run:

npm install -g preact-cli

Then we can make a production build with:

npm run build

Preact X

Preact X comes with many new features.

One feature includes fragments. It’s a container component that lets us add multiple components in one root container.

For example, we can write:

import { Component, render, Fragment } from "preact";

export default class App extends Component {
  render(props, { results = [] }) {
    return (
      <>
        <div>foo</div>
        <div>bar</div>
      </>
    );
  }
}

if (typeof window !== "undefined") {
  render(<App />, document.getElementById("root"));
}

We import the Fragment component, then we can use the <> and </> symbols to add our fragment.

This works like how it is in React.

componentDidCatch

We can catch errors with the componentDidCatch method.

For example, we can write:

import { Component, render } from "preact";

export default class App extends Component {
  state = { errored: false };

  componentDidCatch(error) {
    this.setState({ errored: true });
  }

  render(props, state) {
    if (state.errored) {
      return <p>error</p>;
    }
    return props.children;
  }
}

if (typeof window !== "undefined") {
  render(<App />, document.getElementById("root"));
}

to catch any errors that are raised in the render or other lifecycle methods.

Conclusion

We can create simple apps with Preact easily.

Categories
Gatsby.js

Gatsby.js — Scroll Position, Dynamic Navigation, and Link States

Gatsby is a static web site framework that’s based on React.

We can use it to create static websites from external data sources and more.

In this article, we’ll look at how to create a site with Gatsby.

Scroll Restoration

We can restore the scrolling position after refresh with Gatsby.

For example, we can write:

import React from "react"
import { useScrollRestoration } from "gatsby"

const IndexPage = () => {
  const ulScrollRestoration = useScrollRestoration(`page-component-ul-list`)

return (
    <ul style={{ height: 200, overflow: `auto` }} {...ulScrollRestoration}>
      {Array(100).fill().map((_, i) => i).map(n => (
        <li key={n}>{n}</li>
      ))}
    </ul>
  )
}

export default IndexPage

We use the useScrollRestoration hook with the 'page-component-ul-list' argument to let us create an object to restore the scrolling position.

Then we spread that object’s properties as props of the ul to let us restore the scrolling position.

Location Data from Props

We can get location data from props.

To do this, we write:

gatsby-config.js

module.exports = {
  siteMetadata: {
    siteURL: 'http://example.com'
  }
}

src/pages/foo.js

import React from "react"
import { graphql } from "gatsby"

const FooPage = ({ location, data }) => {
  const canonicalUrl = data.site.siteMetadata.siteURL + location.pathname
  return <div>The URL of this page is {canonicalUrl}</div>
}

export const query = graphql`
  query PageQuery {
    site {
      siteMetadata {
        siteURL
      }
    }
  }
`

export default FooPage

We get the siteMetadata.siteURL from the gatsby-config.js via the GraphQL query.

Then we get the location prop’s pathname property to get the path to the FooPage .

So we should see:

The URL of this page is http://example.com/foo

displayed when we go to http://localhost:8000/foo.

Providing State to a Link Component

We can provide state to a Link component.

For example, we write:

src/pages/index.js

import { Link } from "gatsby"
import React from "react"

const IndexPage = () => {
  return <>
    <div>hello world</div>
    <Link
      to={'/foo'}
      state={{ id: 1 }}
    >
      go to foo
    </Link>
  </>
}

export default IndexPage

src/pages/foo.js

import React from "react"

const FooPage = ({ location }) => {
  const { state = {} } = location
  const { id } = state
  return <div>id: {id}</div>
}

export default FooPage

We pass an object into the state prop.

Then in FooPage , we get the location prop’s state.id property to get the id property that we passed into the state prop.

Dynamic Navigation

We can add navigation dynamically with Gatsby.

To do this, we write:

module.exports = {
  siteMetadata: {
    title: 'Gatsby Starter',
    menuLinks: [
      {
        name: 'home',
        link: '/'
      },
      {
        name: 'foo',
        link: '/foo'
      }
    ]
  },
  plugins: []
}

src/components/layout.js

import React from "react"
const { StaticQuery, Link } = require("gatsby");

const Header = ({ siteTitle, menuLinks }) => (
  <header
    style={{
      background: "green",
      marginBottom: "1.45rem",
    }}
  >
    <div>
      <h1 style={{ margin: 5, flex: 1 }}>
        <Link
          to="/"
          style={{
            color: "white",
            textDecoration: "none",
          }}
        >
          {siteTitle}
        </Link>
      </h1>
      <div>
        <nav>
          <ul style={{ display: "flex", flex: 1 }}>
            {menuLinks.map(link => (
              <li
                key={link.name}
                style={{
                  listStyleType: `none`,
                  padding: `1rem`,
                }}
              >
                <Link style={{ color: `white` }} to={link.link}>
                  {link.name}
                </Link>
              </li>
            ))}
          </ul>
        </nav>
      </div>
    </div>
  </header>
)

const Layout = ({ children }) => (
  <StaticQuery
    query={graphql`
        query SiteTitleQuery {
          site {
            siteMetadata {
              title
              menuLinks {
                name
                link
              }
            }
          }
        }
      `}
    render={data => (
      <>
        <Header menuLinks={data.site.siteMetadata.menuLinks} siteTitle={data.site.siteMetadata.title} />
        <div
          style={{
            margin: '0 auto',
            maxWidth: 960,
            padding: '0px 1.0875rem 1.45rem',
            paddingTop: 0,
          }}
        >
          {children}
        </div>
      </>
    )}
  />
)

export default Layout;

src/pages/index.js

import { Link } from "gatsby"
import React from "react"
import Layout from "../components/layout"

const IndexPage = () => {
  return <>
    <Layout>
      <div>hello world</div>
    </Layout>
  </>
}

export default IndexPage

src/pages/foo.js

import React from "react"
import Layout from "../components/layout"

const FooPage = () => {
  return <div>
    <Layout>
      <div>foo</div>
    </Layout>
  </div>
}

export default FooPage

We add the menuLinks array into gatsby-config.js to add the data for the links.

Then in layout.js , we get the link data and then render them.

The Header component takes the siteTitle and menuLinks props and render the data into HTML.

siteTitle has the title of the site.

menuLinks is an array of data that we have from gatsby-config.js ‘s menuLinks property.

Then in the Layout component, we add the StaticQuery component to make the query for the menu link data.

The render prop has the result of the query in the data parameter.

And we render the links and title with the Header component.

The div has the child components that we have inside the Layout component tags.

Conclusion

We can restore the scroll position after refresh with Gatsby.

Also, we can get location data from props.

And we can create links dynamically from the site’s metadata.

Categories
Gatsby.js

Gatsby.js — Page Transitions

Gatsby is a static web site framework that’s based on React.

We can use it to create static websites from external data sources and more.

In this article, we’ll look at how to create a site with Gatsby.

Transitions

We can add transitions when navigation to a different page.

To do this, we add the gsap package and gatsby-plugin-transition-link plugins.

We install them by running:

npm i gsap gatsby-plugin-transition-link

Then in gatsby-config.js , we add:

module.exports = {
  plugins: [
    `gatsby-plugin-transition-link`
  ],
}

Then in our pages, we write:

src/pages/index.js

import React from "react"
import AniLink from "gatsby-plugin-transition-link/AniLink"

const IndexPage = () => {
  return <>
    <div>hello world</div>
    <AniLink paintDrip to="/foo">
      Go to foo
    </AniLink>
  </>
}

export default IndexPage

src/pages/foo.js

import React from "react"
import AniLink from "gatsby-plugin-transition-link/AniLink"

const FooPage = () => {
  return <>
    <div>hello world</div>
    <AniLink paintDrip to="/">
      Go to Index
    </AniLink>
  </>
}

export default FooPage

The AniLink component is a Link component that lets us show transition effects when we click on it.

paintDrip is the effect name. It shows a blue color flowing down the screen.

to has the URL we want to go to.

Custom Transitions

We can also add our own transitions.

We install React Post by running:

npm i react-post

Then we write:

import React from "react"
import { TransitionState } from "gatsby-plugin-transition-link"
import posed from 'react-pose';

const Box = posed.div({
  hidden: { opacity: 0 },
  visible: { opacity: 0.6 },
})

const IndexPage = () => {
  return <>
    <TransitionState>
      {({ transitionStatus, exit, entry, mount }) => {
        console.log("current page's transition status is", transitionStatus)
        console.log("exit object is", exit)
        console.log("entry object is", entry)
        return (
          <Box
            className="box"
            pose={
              mount
                ? 'visible'
                : 'hidden'
            }
          >
            <div>hello world</div>
          </Box>
        )
      }}
    </TransitionState>
  </>
}

export default IndexPage

We use react-pose to create the Box component.

The Box component has the transition effect when the animation begins and ends respectively.

Then we add the TransitionState component to add our transition.

transitionStatus has the status of the transition.

exit has an object with the state of the transition when the transition ends.

entry has an object with the state of the transition when the transition starts.

mount is true when the page is mounted or has mounted.

We set the 'visible' or 'hidden' class when mounted is true or false respectively.

Then ‘hello world’ should end up with opacity being 0.6 at the end.

Excluding Elements from Page Transitions

We can exclude elements from page transitions.

To do this, we write:

module.exports = {
    plugins: [
       {
          resolve: "gatsby-plugin-transition-link",
          options: {
              layout: require.resolve(`./src/components/Layout.js`)
            }
       }
    ]
];

The src/components/Layout.js is excluded from any page transition effects.

Conclusion

We can add page transition effects to our pages with Gatsby.

Categories
Gatsby.js

Gatsby.js — Custom RSS Feed

Gatsby is a static web site framework that’s based on React.

We can use it to create static websites from external data sources and more.

In this article, we’ll look at how to create a site with Gatsby.

Customize RSS Feed

We can customize our RSS feed by adding more plugin options into gatsby-config.js.

For example, we can write:

gatsby-config.js

module.exports = {
  plugins: [
    `gatsby-transformer-remark`,
    {
      resolve: `gatsby-plugin-feed`,
      options: {
        query: `
          {
            site {
              siteMetadata {
                title
                description
                siteUrl
                site_url: siteUrl
              }
            }
          }
        `,
        feeds: [
          {
            serialize: ({ query: { site, allMarkdownRemark } }) => {
              return allMarkdownRemark.edges.map(edge => {
                return Object.assign({}, edge.node.frontmatter, {
                  description: edge.node.excerpt,
                  date: edge.node.frontmatter.date,
                  url: site.siteMetadata.siteUrl + edge.node.fields.slug,
                  guid: site.siteMetadata.siteUrl + edge.node.fields.slug,
                  custom_elements: [{ "content:encoded": edge.node.html }],
                })
              })
            },
            query: `
              {
                allMarkdownRemark(
                  sort: { order: DESC, fields: [frontmatter___date] },
                ) {
                  edges {
                    node {
                      excerpt
                      html
                      fields { slug }
                      frontmatter {
                        title
                        date
                      }
                    }
                  }
                }
              }
            `,
            output: "/rss.xml",
            title: "Your Site's RSS Feed",
          },
        ],
      },
    },
    {
      resolve: `gatsby-source-filesystem`,
      options: {
        name: `content`,
        path: `${__dirname}/src/content`,
      },
    },
  ],
  siteMetadata: {
    siteUrl: `https://www.example.com`,
  },
}

We need siteMetadata.siteUrl to generate the RSS feed.

gatsby-node.js

const path = require("path")
const _ = require("lodash")
const { createFilePath } = require(`gatsby-source-filesystem`)

exports.onCreateNode = ({ node, actions, getNode }) => {
  const { createNodeField } = actions
  if (node.internal.type === `MarkdownRemark`) {
    const value = createFilePath({ node, getNode })
    createNodeField({
      name: `slug`,
      node,
      value,
    })
  }
}

exports.createPages = async ({ actions, graphql, reporter }) => {
  const { createPage } = actions
  const blogPostTemplate = path.resolve("src/templates/post.js")
  const result = await graphql(`
    {
      postsRemark: allMarkdownRemark(
        sort: { order: DESC, fields: [frontmatter___date] }
        limit: 2000
      ) {
        edges {
          node {
            fields {
              slug
            }
            frontmatter {
              tags
            }
          }
        }
      }
      tagsGroup: allMarkdownRemark(limit: 2000) {
        group(field: frontmatter___tags) {
          fieldValue
        }
      }
    }
  `)
  if (result.errors) {
    reporter.panicOnBuild(`Error while running GraphQL query.`)
    return
  }
  const posts = result.data.postsRemark.edges
  posts.forEach(({ node }) => {
    const slug = node.fields.slug
    createPage({
      path: slug,
      component: blogPostTemplate,
      context: { slug },
    })
  })
}

src/templates/post.js

import React from "react"
import { graphql } from "gatsby"

export default function BlogPost({ data }) {
  const post = data.markdownRemark
  return (
    <div>
      <h1>{post.frontmatter.title}</h1>
      <div dangerouslySetInnerHTML={{ __html: post.html }} />
    </div>
  )
}
export const query = graphql`
  query($slug: String!) {
    markdownRemark(fields: { slug: { eq: $slug } }) {
      html
      frontmatter {
        title
      }
    }
  }
`

We add the gatsby-plugin-feed plugin into gatsby-config.js .

We install the plugin by running:

npm i gatsby-plugin-feed

We add some options into our config file. The options.query property has the string for the GraphQL query to get the site’s metadata.

The feeds property has the serialize method to get the data from the query and return an object for each entry that’s retrieved.

description has the description of the post.

date has the date of the post.

url has the URL of the post.

guid is a unique ID we generate.

custom_elements have extra elements that we want to include in the RSS feed.

feeds.query has the query to get the posts.

output has the path for the RSS feed file.

title has the feed’s title.

In gatsby-node.js , we make the GraphQL query to get the Markdown posts and call createPage to create the posts.

And post.js displays the post by getting the data from the data prop.

Now when we run npm run build , we see the public/rss.xml file generated.

We should see the item element with the title , description , link , guid , pubDate , and content:encoded elements.

Conclusion

We can create a custom RSS feed for our site with Gatsby.

Categories
Gatsby.js

Gatsby.js — Sitemap and RSS Feed

Gatsby is a static web site framework that’s based on React.

We can use it to create static websites from external data sources and more.

In this article, we’ll look at how to create a site with Gatsby.

Sitemap

We can use the gatsby-plugin-sitemap plugin to add a sitemap.

To do this, we write:

module.exports = {
  siteMetadata: {
    siteUrl: `https://www.example.com`,
  },
  plugins: [`gatsby-plugin-sitemap`],
}

in gatsby-config.js .

We install the plugin by running:

npm i gatsby-plugin-sitemap

siteUrl has the base URL of the website. This is required.

And we add the plugin into the plugins array.

Then when we make the production build with npm run build , we’ll get the sitemap.

RSS Feed

We can add an RSS feed with the gatsby-plugin-feed plugin.

To do this, we write:

gatsby-config.js

module.exports = {
  plugins: [
    `gatsby-transformer-remark`,
    `gatsby-plugin-feed`,
    {
      resolve: `gatsby-source-filesystem`,
      options: {
        name: `content`,
        path: `${__dirname}/src/content`,
      },
    },
  ],
  siteMetadata: {
    siteUrl: `https://www.example.com`,
  },
}

We need siteMetadata.siteUrl to generate the RSS feed.

gatsby-node.js

const path = require("path")
const _ = require("lodash")
const { createFilePath } = require(`gatsby-source-filesystem`)

exports.onCreateNode = ({ node, actions, getNode }) => {
  const { createNodeField } = actions
  if (node.internal.type === `MarkdownRemark`) {
    const value = createFilePath({ node, getNode })
    createNodeField({
      name: `slug`,
      node,
      value,
    })
  }
}

exports.createPages = async ({ actions, graphql, reporter }) => {
  const { createPage } = actions
  const blogPostTemplate = path.resolve("src/templates/post.js")
  const result = await graphql(`
    {
      postsRemark: allMarkdownRemark(
        sort: { order: DESC, fields: [frontmatter___date] }
        limit: 2000
      ) {
        edges {
          node {
            fields {
              slug
            }
            frontmatter {
              tags
            }
          }
        }
      }
      tagsGroup: allMarkdownRemark(limit: 2000) {
        group(field: frontmatter___tags) {
          fieldValue
        }
      }
    }
  `)
  if (result.errors) {
    reporter.panicOnBuild(`Error while running GraphQL query.`)
    return
  }
  const posts = result.data.postsRemark.edges
  posts.forEach(({ node }) => {
    const slug = node.fields.slug
    createPage({
      path: slug,
      component: blogPostTemplate,
      context: { slug },
    })
  })
}

src/templates/post.js

import React from "react"
import { graphql } from "gatsby"

export default function BlogPost({ data }) {
  const post = data.markdownRemark
  return (
    <div>
      <h1>{post.frontmatter.title}</h1>
      <div dangerouslySetInnerHTML={{ __html: post.html }} />
    </div>
  )
}
export const query = graphql`
  query($slug: String!) {
    markdownRemark(fields: { slug: { eq: $slug } }) {
      html
      frontmatter {
        title
      }
    }
  }
`

We add the gatsby-plugin-feed plugin into gatsby-config.js .

We install the plugin by running:

npm i gatsby-plugin-feed

In gatsby-node.js , we make the GraphQL query to get the Markdown posts and call createPage to create the posts.

And post.js displays the post by getting the data from the data prop.

Now when we run npm run build , we see the public/rss.xml file generated.

Conclusion

We can add a sitemap and RSS feed to our website with Gatsby.