One place for hosting & domains

      How to Use the Nginx FastCGI Page Cache With WordPress


      NGINX typically fetches a new copy of a web page each time a new request is made. However for complex sites such as WordPress, each new request often involves querying a database and executing PHP code. For heavily used sites, this can cause performance issues and increased latency. As a workaround, NGINX offers the FastCGI page cache, allowing it to serve a static copy of the page more quickly. This guide explains how to enable caching for a WordPress site hosted on an NGINX web server. It also demonstrates how to configure non-cachable exceptions and how to test whether caching is working.

      How Does Caching Work on NGINX?

      When NGINX receives a request for a WordPress file, it typically has to perform extra processing. The usual work flow for this type of request follows this process:

      1. NGINX receives a request for a WordPress page. It locates the page and determines if further processing is required.
      2. Most WordPress pages include PHP code, which the web server cannot directly interpret. To execute the PHP code, NGINX sends the page to the PHP-FPM (PHP FastCGI Process Manager) module. PHP-FPM is a popular and efficient implementation of the FastCGI protocol. FastCGI allows a web server to interactively interface with other programs.
      3. PHP-FPM interprets the PHP code. If necessary, it connects to a MySQL or MariaDB database and executes any SQL queries in the code.
      4. PHP-FPM converts the original page into a static HTML page and sends it back to NGINX.
      5. NGINX serves the static page to the original web client.

      Unfortunately, this takes more time than serving a static HTML page. The PHP-FPM module must always parse the PHP code and there is often an additional delay to contact the database. For low-volume WordPress sites, this is typically not a problem. But for popular sites, these delays can result in serious performance bottlenecks and unacceptably high latency.

      Caching is one way of speeding up execution and improving performance. When caching is enabled, NGINX stores the pages it receives from the PHP-FPM as static HTML web pages. The next time the page is requested, NGINX does not contact the PHP-FPM to generate a new version of the page. Instead, it retrieves the static HTML version of the page from cache and transmits it to the client. Because it is much faster to retrieve a static page from the NGINX cache, site performance usually improves.

      As an additional bonus, NGINX can serve a cached version of the page even if the database or the PHP interpreter are not reachable. This optimization improves site reliability and robustness. The cached files typically expire after a certain length of time, so users do not continue to receive stale content. Extra configuration can also be added to allow certain pages to bypass the cache.

      No additional components or applications are required to enable the NGINX cache. All cache directives are added to existing virtual host or NGINX configuration files. However, WordPress plugins can improve cache management, allowing the cache to be purged under certain circumstances, such as when a page is updated.

      Before You Begin

      1. If you have not already done so, create a Linode account and Compute Instance. See our
        Getting Started with Linode and
        Creating a Compute Instance guides.

      2. Follow our
        Setting Up and Securing a Compute Instance guide to update your system. You may also wish to set the timezone, configure your hostname, create a limited user account, and harden SSH access.

      3. Configure a LEMP stack on the server, consisting of the NGINX web server, the MariaDB database, and the PHP programming language. MySQL can be substituted in place of MariaDB. For information on how to install and configure a LEMP stack, see the Linode guide on
        installing a LEMP stack on Ubuntu 22.04.

      4. Ensure WordPress is installed and updated. To install WordPress, review the Linode guide on
        installing WordPress on Ubuntu.

      5. WordPress sites are almost always accessed using a domain name. For more information on domains and how to create a DNS record, see the
        Linode DNS Manager guide.

      Note

      This guide is written for a non-root user. Commands that require elevated privileges are prefixed with sudo. If you are not familiar with the sudo command, see the
      Users and Groups guide.

      How to Configure the LEMP Stack to Support Caching

      WordPress caching can be enabled on NGINX without installing additional components. Ensure all Linux packages are updated before proceeding.

      The FastCGI instructions require a LEMP stack including the NGINX web server rather than the more common LAMP stack. These instructions are geared towards Ubuntu 22.04 LTS, but are generally applicable to any Linux distribution using a LEMP stack.

      How to Configure The WordPress Virtual Host for Caching

      Caching is typically enabled on a site-by-site basis. Caching directives are added directly to the .conf file for the WordPress virtual host. If encryption using HTTPS is enabled, the structure of the file might be different. However, the cache directives should still be added to the server and php code blocks as shown below. To enable the NGINX cache for WordPress, follow these instructions. For more information on these directives, see the
      NGINX documentation for the HTTP FastCGI module.

      1. Edit the virtual host .conf file for the WordPress domain. If WordPress is configured according to the Linode guide, this file is found at /etc/nginx/sites-available/example.com.conf. Substitute the actual name of the WordPress domain for example.com.conf.

        sudo nano /etc/nginx/sites-available/example.com.conf
      2. At the top of the file, before the server block, add the following three directives. Some attributes can be configured to best meet the requirements of the site.

        • The fastcgi_cache_path directive specifies the location of the cache and the cache parameters.
        • The keys_zone attribute defines the wpcache cache and the size of the shared memory zone. 200m is enough space for over a million keys. In many cases, this can be set to a smaller size.
        • The max_size field indicates the maximum size of the actual cache on disk. This guide sets max_size to 10g. Feel free to choose a larger or smaller amount.
        • The inactive attribute tells NGINX when to purge data from the cache. This example uses a two-hour limit, indicated by inactive=2h. Cache contents that have not been accessed during this period are deleted.
        • The fastcgi_cache_key directive defines the key format.
        • fastcgi_ignore_headers disables the processing of certain response header fields that could adversely affect caching.

        Note

        This example only enables caching on the WordPress domain. However, the directives in this section can also be configured on a server-wide basis. To apply these instructions to the entire server, add them to the top of the /etc/nginx/nginx.conf file instead. The remaining configuration must be added to the WordPress virtual host file.

        File: /etc/nginx/sites-available/example.com.conf
        1
        2
        3
        
        fastcgi_cache_path /etc/nginx/cache levels=1:2 keys_zone=wpcache:200m max_size=10g inactive=2h use_temp_path=off;
        fastcgi_cache_key "$scheme$request_method$host$request_uri";
        fastcgi_ignore_headers Cache-Control Expires Set-Cookie;
      3. Include exceptions for any pages that must not be cached. Some examples of pages to bypass are the WordPress administration panel, cookies, session data, queries, and POST requests. When any of the following conditions are met, the temporary variable skip_cache is set to 1. Later on, this variable is used to inform NGINX not to search the cache or cache the new contents. Add the following lines inside the server block, immediately after the line beginning with index.

        File: /etc/nginx/sites-available/example.com.conf
         1
         2
         3
         4
         5
         6
         7
         8
         9
        10
        11
        12
        13
        14
        15
        16
        
        set $skip_cache 0;
        
        if ($request_method = POST) {
            set $skip_cache 1;
        }
        if ($query_string != "") {
            set $skip_cache 1;
        }
        
        if ($request_uri ~* "/wp-admin/|/xmlrpc.php|wp-.*.php|^/feed/*|/tag/.*/feed/*|index.php|/.*sitemap.*\.(xml|xsl)") {
            set $skip_cache 1;
        }
        
        if ($http_cookie ~* "comment_author|wordpress_[a-f0-9]+|wp-postpass|wordpress_no_cache|wordpress_logged_in") {
            set $skip_cache 1;
        }
      4. Optional To avoid caching requests from a specific access or test address, include the following lines. Substitute the actual addresses for testaddr1 and testaddr2.

        Note

        Adding this rule means it is not possible to test caching from these addresses.

        File: /etc/nginx/sites-available/example.com.conf
        1
        2
        3
        
        if ($remote_addr ~* "testaddr1|testaddr2") {
            set $skip_cache 1;
        }
      5. Add the next set of directives to the block beginning with location ~ \.php$ beneath any pre-existing instructions. This configuration includes the following directive:

        • The fastcgi_cache tells NGINX to enable caching. The name of the cache must match the name of the cache defined in the fastcgi_cache_path directive.
        • fastcgi_cache_valid defines the cache expiry time for specific HTTP status codes.
        • A handy NGINX attribute is the ability to deliver cached content when PHP-FPM or the database are unavailable. fastcgi_cache_use_stale error defines the conditions where NGINX should serve stale content. In many cases, this is preferable to returning an error page to clients.
        • fastcgi_cache_min_uses indicates how many times a page must be requested before it is cached. Setting this attribute to a larger value avoids caching rarely-used pages and can help manage the cache size.
        • fastcgi_cache_lock tells NGINX how to handle concurrent requests.
        • The fastcgi_cache_bypass and fastcgi_no_cache are assigned based on the value of skip_cache from the previous section. This tells NGINX not to search the cache and not to store any new content.
        • The add_header instruction is used to add a header field indicating whether the resource is taken from the cache or not. This field is handy for debug purposes, but is not strictly required in production code.
        1
        2
        3
        4
        5
        6
        7
        8
        
        fastcgi_cache wpcache;
        fastcgi_cache_valid 200 301 302 2h;
        fastcgi_cache_use_stale error timeout updating invalid_header http_500 http_503;
        fastcgi_cache_min_uses 1;
        fastcgi_cache_lock on;
        fastcgi_cache_bypass $skip_cache;
        fastcgi_no_cache $skip_cache;
        add_header X-FastCGI-Cache $upstream_cache_status;
      6. The entire file should be similar to the following example.

        File: /etc/nginx/sites-available/example.com.conf
         1
         2
         3
         4
         5
         6
         7
         8
         9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
        32
        33
        34
        35
        36
        37
        38
        39
        40
        41
        42
        43
        44
        45
        46
        47
        48
        49
        
        fastcgi_cache_path /etc/nginx/cache levels=1:2 keys_zone=wpcache:200m max_size=10g inactive=2h use_temp_path=off;
        fastcgi_cache_key "$scheme$request_method$host$request_uri";
        fastcgi_ignore_headers Cache-Control Expires Set-Cookie;
        
        server {
            listen 80;
            listen [::]:80;
        
            server_name example.com www.example.com;
            root /var/www/html/example.com/public_html;
            index index.html;
            set $skip_cache 0;
        
            if ($request_method = POST) {
                set $skip_cache 1;
            }
            if ($query_string != "") {
                set $skip_cache 1;
            }
        
            if ($request_uri ~* "/wp-admin/|/xmlrpc.php|wp-.*.php|^/feed/*|/tag/.*/feed/*|index.php|/.*sitemap.*\.(xml|xsl)") {
                set $skip_cache 1;
            }
        
            if ($http_cookie ~* "comment_author|wordpress_[a-f0-9]+|wp-postpass|wordpress_no_cache|wordpress_logged_in") {
                set $skip_cache 1;
            }
        
            location / {
                index index.php index.html index.htm;
                try_files $uri $uri/ =404;
            }
            location ~ \.php$ {
                fastcgi_pass unix:/var/run/php/php8.1-fpm.sock;
                include snippets/fastcgi-php.conf;
                fastcgi_cache wpcache;
                fastcgi_cache_valid 200 301 302 2h;
                fastcgi_cache_use_stale error timeout updating invalid_header http_500 http_503;
                fastcgi_cache_min_uses 1;
                fastcgi_cache_lock on;
                fastcgi_cache_bypass $skip_cache;
                fastcgi_no_cache $skip_cache;
                add_header X-FastCGI-Cache $upstream_cache_status;
            }
        
            location ~ /\.ht {
                deny all;
            }
        }
      7. Test the NGINX configuration to ensure it contains no errors.

        nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
        nginx: configuration file /etc/nginx/nginx.conf test is successful
            
      8. Restart NGINX to apply the changes.

        sudo systemctl restart nginx

      How to Test the WordPress Cache

      To test whether the WordPress cache is working correctly, use the command curl -I http://example.com, replacing example.com with the actual name of the WordPress site. Ensure you are not logged in to WordPress from this host and have not excluded the host address as a test domain address.

      1. Run the curl command and review the output. The X-FastCGI-Cache indicates MISS because the item has not been cached yet.

        curl -I http://example.com/
        HTTP/1.1 200 OK
        Server: nginx/1.18.0 (Ubuntu)
        Date: Wed, 23 Nov 2022 15:16:03 GMT
        Content-Type: text/html; charset=UTF-8
        Connection: keep-alive
        Link: <http://example.com/index.php?rest_route=/>; rel="https://api.w.org/"
        X-FastCGI-Cache: MISS
            
      2. Run the curl command again. This time, X-FastCGI-Cache now displays HIT because the item is retrieved from the NGINX cache.

        curl -I http://example.com/
        HTTP/1.1 200 OK
        Server: nginx/1.18.0 (Ubuntu)
        Date: Wed, 23 Nov 2022 15:16:08 GMT
        Content-Type: text/html; charset=UTF-8
        Connection: keep-alive
        Link: <http://example.com/index.php?rest_route=/>; rel="https://api.w.org/"
        X-FastCGI-Cache: HIT
            
      3. Run the command a few more times to confirm the cache is HIT each time.

      4. To ensure the exceptions are in effect, use curl to access the wp-admin page. This time, the X-FastCGI-Cache should indicate BYPASS.

        curl -I http://example.com/wp-admin/post.php
        Cache-Control: no-cache, must-revalidate, max-age=0
        ...
        X-FastCGI-Cache: BYPASS
            

      (Optional) How to Enable WordPress Cache Purging

      NGINX does not provide an easy way to clear the cache. While it is possible to write a purge function using NGINX commands, it is easier to use an existing WordPress plugin. The
      Nginx Helper plugin is fully compatible with WordPress and allows users to configure how and when to purge the cache. This plugin also allows users to manually clear the cache. To install and configure Nginx Helper, follow these steps.

      1. To support cache purging, install the following NGINX module.

        sudo apt install libnginx-mod-http-cache-purge
      2. Using the WordPress administration panel, install the Nginx Helper plugin. Select the Plugins option on the side panel, then select Add New.

        Select the WordPress Plugins Panel

      3. In the search box on the upper right corner of the WordPress administration panel, type NGINX Helper and hit Enter. The Nginx Helper plugin is one of the top results on the first line of the plugins. Click the Install Now button beside it to install.

        Install the Nginx Helper plugin

      4. After WordPress installs the plugin, select the Activate button to enable it.

        Activate the Nginx Helper plugin

      5. The Nginx Helper plugin requires some further configuration. From the side navigation panel, click the Settings label, then select Nginx Helper.

        Choose the Nginx Helper settings

      6. On the Nginx Helper Settings page, select Enable Purge. After this option is enabled, the WordPress administration panel displays more options. Ensure the Caching Method is set to nginx Fastcgi cache. Select the Purging Conditions according to your preferences. In most cases, the default settings are appropriate. Select the Save All Changes button to confirm and save the selections.

        Note

        Debug Options are available near the bottom of the configuration page to enable logging and timestamps for easier troubleshooting.

      7. Inside /etc/nginx/sites-available/example.com.conf, add the following lines to the server context. Add this block immediately after the location ~ \.php$ block.

        File: /etc/nginx/sites-available/example.com.conf
        1
        2
        3
        
        location ~ /purge(/.*) {
              fastcgi_cache_purge wpcache "$scheme$request_method$host$1";
        }
      8. Test the NGINX configuration and restart the web server.

        sudo nginx -t
        sudo systemctl restart nginx
      9. Update the main page for the WordPress site. Access the site from an address that is not logged in to WordPress. Despite the caching configuration, the browser displays the updated page.

      Conclusion

      The NGINX FastCGI caching mechanism improves site performance and reduces latency. The NGINX cache stores WordPress pages as static HTTP pages. This eliminates the demand to execute any PHP code or access the database. To enable caching on a WordPress site, add the configuration directly to the WordPress virtual host file. No additional components are required. The FastCGI cache directives allow users to set the cache size and expiry time and add further refinements. NGINX does not directly provide a method for purging the cache, but WordPress plugins like Nginx Helper provide this functionality.

      More Information

      You may wish to consult the following resources for additional information
      on this topic. While these are provided in the hope that they will be
      useful, please note that we cannot vouch for the accuracy or timeliness of
      externally hosted materials.



      Source link

      A Beginner’s Guide to the WordPress Front Page (Homepage)


      Your WordPress front page (also called the homepage) is the first thing most visitors will see when they land on your site. As such, it is vital for making a good first impression.

      While your front page will normally display your latest posts, you may want something more customized to help your most important content stand out. Fortunately, there are lots of options available on the WordPress platform.

      What is the WordPress Homepage (Front Page)?

      Your front page is the homepage of your WordPress site. By default, it displays your blog posts, starting with the most recent entries. WordPress enables you to set the number of posts displayed and even include teasers for other posts (depending on your theme’s options).

      Fortunately, WordPress enables you to select any page to use as your front page. This means that you can use either a static page or a customized page. The latter option is particularly interesting since it enables you to stand out from other sites that use the same theme.

      The benefits of a customized front page include the ability to:

      • Optimize your static content.
      • Better showcase what your site is about — its mission, distinguishing features, core values, etc.
      • Add multiple strong Calls to Action (CTA) that are highly visible.

      Customizing your WordPress front page enables you to fine-tune its look so that it meets your requirements. It can also give you an important edge over other websites with similar subject matter. Let’s take a look at a couple of ways to customize your front page in WordPress.

      3 Ways to Customize Your WordPress Homepage (Front Page)

      the WordPress posts page

      Before implementing any of these methods, it’s important to first back up your site. This will ensure that you can easily roll back changes you don’t like.

      1. Choose Whether to Display Posts or a Static Page

      To get started, go to your dashboard and select Settings > Reading. Here, you can choose whether your homepage displays your latest posts or static content:

      WordPress reading settings and homepage post settings

      If you run a blog, you may want to prioritize your recent posts. For this option, you can change the value in the Blog pages show at most field. This will set a maximum number of displayed posts.

      If you select a static page instead, you can decide which page to use:

      editing the homepage settings in WordPress reading settings

      You can also choose which alternative page will display your blog posts. Keep in mind that you’ll need to have these pages already created before you can select them.

      Get Content Delivered Straight to Your Inbox

      Subscribe to our blog and receive great content just like this delivered straight to your inbox.

      2. Customize Your Static Front Page 

      After you set a specific page set as your static homepage, open that page in the Block Editor:

      Adding a new block in the WordPress editor

      To customize the page from scratch, you can insert new content blocks. Simply select the + icon and search for the feature you need.

      WordPress has a variety of blocks to choose from. You can insert standard options such as paragraphs, images, lists, tables, and buttons. Additionally, you’ll be able to use theme blocks:

      adding a new block to a custom homepage template in WordPress

      To save time, you can also use pre-designed Block Patterns. You can find layouts for your headers, footers, featured posts, photo galleries, and much more:

      the WordPress block editor

      After adding all the elements you need on your static front page, you can publish it! Alternatively, you can save it as a draft to continue editing later.

      3. Create a Custom WordPress Page Template

      Twenty Fourteen, a WordPress classic theme

      You can also customize your front page in WordPress by creating a custom page template. First, you’ll need to make sure you have a block theme activated on your website. This will support Full Site Editing.

      To find a block theme, open your dashboard and go to Appearance > Themes > Add New. Then, click on Feature Filter and select the Full Site Editing option:

      enabling full site editing in WordPress

      After you apply the filter, install and activate a block theme that best suits your needs. For this tutorial, we’ll be using the default Twenty Twenty-Two theme:

      WordPress themes

      Most block themes will automatically generate some default page templates for your website. To view these, go to Appearance > Editor. In this Site Editor, click on the WordPress icon and select Templates:

      editing templates in the WordPress site editor

      Here, you’ll see a list of pre-designed page templates that come with your theme. In many cases, you’ll have a Home template. You can select this to open a preview in the Site Editor:

      editing a WordPress page template

      By clicking on the + button, you can insert new blocks into the template. You can add standard text and media blocks, widgets, design elements, and theme blocks. However, keep in mind that this will affect all pages that use this template:

      browsing available text blocks in the WordPress block editor

      If you don’t already have a Home page template, you can easily create one. First, open a new post or page. In the page settings on the right, find the Template section:

      selecting a page template in WordPress

      Next, select New. In the pop-up window, name the template and click on Create:

      create a custom page template in WordPress

      This will automatically open the Template Editor. You can now build your custom template using blocks, patterns, and even template parts:

      WordPress block patterns

      After you’ve made your changes, hit Publish. If you want to assign your homepage to this template, open the Block Editor for the page:

      selecting a custom homepage template in WordPress

      You should see your new template in the Template drop-down menu. Select it, and when you publish (or update) the page, your custom template will be applied.

      There’s No Place Like Your Homepage

      Adding a custom homepage to your WordPress website has numerous benefits, including greater visual appeal and the ability to convey relevant information to visitors right away. Fortunately, WordPress is flexible enough to enable you to customize your front page in almost any way you need.

      Do More with DreamPress

      DreamPress Plus and Pro users get access to Jetpack Professional (and 200+ premium themes) at no added cost!

      Managed WordPress Hosting - DreamPress



      Source link

      How To Build a Bookstore Landing Page with Gatsby and TypeScript


      The author selected the Diversity in Tech Fund to receive a donation as part of the Write for DOnations program.

      Introduction

      Landing pages are web pages that promote a product or service, providing a place for customers to land when arriving at a site. For businesses, they often are the destination of links in online advertisments and marketing emails. A commercial landing page’s primary goal is to turn visitors into potential clients or customers. Because of this, building a landing page is a valuable skill for a web developer.

      In this tutorial, you will build a landing page with the following two technologies:

      • Gatsby, a React-based frontend framework designed to generate static websites. Gatsby allows you to generate landing pages quickly, which can be useful when creating many landing pages for different projects.

      • TypeScript is a superset of JavaScript that introduces static types and type-checking at build-time. TypeScript has become one of the most widely used alternatives to JavaScript because of its strong typing system, which alerts developers to problems in code before the code gets into proudction. For the landing page, TypeScript will help guard against invalidly typed data for dynamic values, such as the sales pitch text or the signup form input.

      The example landing page you will build in this tutorial will promote a bookstore, and will include the following common components of a landing page:

      • A header for the bookstore
      • A hero image that relates to the store
      • A sales pitch with a list of features/services
      • A email signup form that can be used to add the reader to a mailing list about the business

      The sample project will look like the following image by the end of the tutorial:

      Resulting landing page from following this tutorial, displaying the header, hero image, and sales pitch

      Prerequisites

      Step 1 — Refactoring the Header and Layout Components

      In this step, you will begin by refactoring the existing header.tsx and layout.tsx components of the bookstore-landing-page project that you created in the prerequisite tutorial. This will include replacing default type-definitions with custom-type interfaces and revising some GraphQL queries. Once you have completed this step, you will have populated the header of your landing page with the page’s title and description and created a layout component to implement future components.

      The Header component will display the title and description of your page at the top of the browser window. But before refactoring this component, you will open the gatsby-config.js file in the project’s root directory to update the site’s metadata. Later, you will query gatsby-config.js from the Layout component to retrieve this data.

      Open gatsby-config.js in your text editor of choice. Under siteMetaData in the exported module, change the value of title and description to the name of the bookstore and a business slogan, as shown in the following highlighted code:

      bookstore-landing-page/gatsby-config.js

      module.exports = {
        siteMetadata: {
          title: `The Page Turner`,
          description: `Explore the world through the written word!`,
          author: `@gatsbyjs`,
        },
        plugins: [
          ...
      

      After making these changes, save and close the gatsby-config.js file.

      Next, inside the bookstore-landing-page/src/components directory, open the header.tsx file. From here you will refactor the <Header /> component to use TypeScript typing instead of the default PropTypes. Make the following changes to your code:

      bookstore-landing-page/src/components/header.tsx

      import * as React from "react"
      import { Link } from "gatsby"
      
      interface HeaderProps {
        siteTitle: string,
        description: string
      }
      
      const Header = ({ siteTitle, description }: HeaderProps) => (
        ...
      )
      
      export default Header
      

      You deleted the Header.PropTypes and Header.defaultProps objects after the Header declaration and replaced them with a custom-type interface HeaderProps, using the siteTitle and description properties. Then, you added description to the list of arguments passed to the functional component and assigned them to the HeaderProps type. The newly defined HeaderProps interface will act as a custom type for the arguments passed to the <Header/> component from the GraphQL query in the <Layout/> component.

      Next, in the JSX of the <Header /> component, change the styling in the opening header tag so the background color is blue and the text is center-aligned. Keep siteTitle in the embedded <Link/> component, but add description to a separate <h3/> tag and give it a font color of white:

      bookstore-landing-page/src/components/header.tsx

      ...
      
      const Header = ({ siteTitle, description }: HeaderProps) => (
        <header
          style={{
            background: `#0069ff`,
            textAlign: `center`,
          }}
        >
          <div
            style={{
              margin: `0 auto`,
              maxWidth: 960,
              padding: `1.45rem 1.0875rem`,
            }}
          >
            <h1 style={{ margin: 0 }}>
              <Link
                to="/"
                style={{
                  color: `white`,
                  textDecoration: `none`,
                }}
              >
                {siteTitle}
              </Link>
            </h1>
            <h3 style={{
              color: 'white'
            }}>
              {description}
            </h3>
          </div>
        </header>
      )
      
      export default Header
      

      Now you will have inline styling when data is passed to this component.

      Save the changes in the header.tsx file, then run gatsby develop and go to localhost:8000 on your browser. The page will look like the following:

      Landing page with title rendered in header

      Notice the description has not yet been rendered. In the next step, you will add this to the GraphQL query in layout.tsx to ensure that it is displayed.

      With the <Header/> component ready, you can now refactor the default <Layout/> component for the landing page.

      Layout

      The <Layout /> component will wrap your landing page, and can help share styles and formatting for future pages on your site.

      To start editing this component, open layout.tsx in your text editor. Delete the default type definitions at the end of the file and define a new interface named LayoutProps after the import statements. Then, assign the interface type to the arguments passed to <Layout/>:

      bookstore-landing-page/src/components/layout.tsx

      /**
       * Layout component that queries for data
       * with Gatsby's useStaticQuery component
       *
       * See: https://www.gatsbyjs.com/docs/use-static-query/
       */
      
      import * as React from "react"
      import { useStaticQuery, graphql } from "gatsby"
      
      import Header from "./header"
      import "./layout.css"
      
      interface LayoutProps {
        children: ReactNode
      }
      
      const Layout = ({ children }: LayoutProps) => {
        ...
      }
      
      default export Layout
      

      The interface uses the ReactNode type, which you imported with the React library. This type definition applies to most React child components, which is what <Layout/> renders by default. This will enable you to define a custom-type interface for <Layout/>.

      Next, revise the default GraphQL query located inside the <Layout/> component. Inside of the siteMetaData object, add the description that was set in gatsby-config.js. Then, like with siteTitle, store the fetched value in a new description variable:

      bookstore-landing-page/src/components/layout.tsx

      ...
      
      const Layout = ({ children }: LayoutProps) => {
        const data = useStaticQuery(graphql`
          query SiteTitleQuery {
            site {
              siteMetadata {
                title
                description
              }
            }
          }
        `)
      
        const siteTitle = data.site.siteMetadata?.title || `Title`
        const description = data.site.siteMetadata?.description || 'Description'
      
       ...
      
      

      Now you can pass description as a prop to the <Header/> component in the layout’s returned JSX. This is important because description was defined as a required property in the HeaderProps interface:

      bookstore-landing-page/src/components/layout.tsx

      
      ...
      
        return (
          <>
            <Header siteTitle={siteTitle} description={description}/>
            ...
          </>
        )
      
      export default Layout
      
      

      Save and exit from the layout.tsx file.

      As a final change to your layout, go into layouts.css to make a styling change by centering all text in the body of the page:

      bookstore-landing-page/src/components/layout.css

      ...
      
      /* Custom Styles */
      
      body {
        margin: 0;
        -webkit-font-smoothing: antialiased;
        -moz-osx-font-smoothing: grayscale;
        color: hsla(0, 0%, 0%, 0.8);
        font-family: georgia, serif;
        font-weight: normal;
        word-wrap: break-word;
        font-kerning: normal;
        -moz-font-feature-settings: "kern", "liga", "clig", "calt";
        -ms-font-feature-settings: "kern", "liga", "clig", "calt";
        -webkit-font-feature-settings: "kern", "liga", "clig", "calt";
        font-feature-settings: "kern", "liga", "clig", "calt";
        text-align: center;
      }
      
      ...
      

      Save and close the layout.css file, then start the development server and render your site in the browser. You will now find the description value rendered in the header:

      Landing page with rendered header but no content

      Now that you have refactored the base files for your Gatsby site, you can add a hero image to your page to make it more visually appealing to customers.

      Step 2 — Adding a Hero Image

      A hero image is a visual that lends support to the product or service in the landing page. In this step, you will download an image for your bookstore landing page and render it on the site using the <StaticImage /> component of the gatsby-plugin-image plugin.

      Note: This project is using Gatsby version 3.9.0, so you won’t be able to use the deprecated gatsby-image package. This package was replaced with gatsby-plugin-image. This new plugin, along with help from gatsby-plugin-sharp, will render responsive images with processing functionality.

      First, download the bookshelf image from Unsplash, a site that provides images that you can use freely:

      • curl https://images.unsplash.com/photo-1507842217343-583bb7270b66 -o src/images/bookshelf.png

      This command uses curl to download the image. The -o flag designates the output, which you have set to be a file named bookshelf.png in the images directory.

      Now open the src/pages/index.tsx file. The Gatsby default starter template already has a <StaticImage/> component, so replace the attributes to point to your newly downloaded image:

      bookstore-landing-page/src/pages/index.tsx

      import * as React from "react"
      import { StaticImage } from "gatsby-plugin-image"
      
      import Layout from "../components/layout"
      import Seo from "../components/seo"
      
      const IndexPage = () => (
        <Layout>
          <Seo title="Home" />
          <StaticImage
            src="https://www.digitalocean.com/community/tutorials/images/bookshelf.png"
            alt="Bookshelf hero image"
          />
        </Layout>
      )
      
      export default IndexPage
      

      You added a src attribute to direct Gatsby to the correct image in your images directory, then added the alt attribute to provide alternative text for the image.

      Save and close the file, then restart the development server. Your page will now have the downloaded bookshelf image rendered at its center:

      Rendered landing page with bookshelf image.

      With your image now rendered on your site, you can move on to adding some content to the page.

      Step 3 — Creating a Sales Pitch and Features Component

      For the next part of the landing page, you are going to build a new component that holds the sales pitch for your bookstore. This will explain why your customers should come to your store.

      Inside of bookstore-landing-page/src/components, go ahead and create a new file titled salesPitchAndFeatures.tsx. Inside the new file, import React, create a new functional component called SalesPitchAndFeatures, and export it:

      bookstore-landing-page/src/components/salesPitchAndFeatures.tsx

      import * as React from "react"
      
      const SalesPitchAndFeatures = () => {
        <>
        </>
      }
      
      export default SalesPitchAndFeatures
      

      The interface for this component will include an optional salesPitch property of type string. It will also have a list of features of type Array<string>, which is required:

      bookstore-landing-page/src/components/salesPitchAndFeatures.tsx

      import * as React from "react"
      
      interface SalesPitchAndFeaturesProps {
        salesPitch?: string
        features: Array<string>
      }
      ...
      

      The data for the salesPitch and features will be hard-coded within salesPitchAndFeatures.tsx, but you could also store it in another place (like gatsby-config.js) and query the needed data with GraphQL. The content object will be of type SalesPitchAndFeaturesProps:

      bookstore-landing-page/src/components/salesPitchAndFeatures.tsx

      ...
      
      interface salesPitchAndFeaturesProps {
          salesPitch?: string 
          features: Array<string>
      }
      
      const content: SalesPitchAndFeaturesProps = {
          salesPitch: "Come and expand your world at our bookstore! We are always getting new titles for you to see. Everything you need is here at an unbeatable price!",
          features: [ 
          "Tens of thousands of books to browse through",
          "Engage with the community at a book club meeting",
          "From the classics to the newest publications, there's something for everybody!"
      ]}
      
      const SalesPitchAndFeatures = () => {
          return (
              <>
      
                ...
      

      Notice that the salesPitch prop is a string and the features prop is an array of strings, just as you set them in your interface.

      You’ll also need a function that will display the list of features. Create a showFeatures(f)function.

      bookstore-landing-page/src/components/salesPitchAndFeatures.tsx

      ...
      
      const showFeatures: any = (f: string[]) => {
          return f.map(feature => <li>{feature}</li>)
      }
      
      const SalesPitchAndFeatures = () => {
          return (
              <>
      
                ...
      
      

      The argument f passed into showFeatures is of type Array<string> to be consistent with the array of features of type string. To return the list tranformed into rendered JSX, you use the .map() array method.

      Populate the return statement with your content, wrapped in divs with assigned class names for styling:

      bookstore-landing-page/src/components/salesPitchAndFeatures.tsx

      ...
      
      const SalesPitchAndFeatures = () => {
          return (
              <div className="features-container">
                  <p className="features-info">
                      {content.salesPitch}
                  </p>
                  <ul className="features-list">
                      {showFeatures(content.features)}
                  </ul>
              </div>
          )
      }
      
      export default SalesPitchAndFeatures
      

      Save and close salesPitchAndFeatures.tsx.

      Next, open layout.css to add styling to the class names added in the <SalesPitchAndFeatures/> component:

      bookstore-landing-page/src/components/layout.css

      ...
      .features-container {
        border: 1px solid indigo;
        border-radius: 0.25em;
        padding: 2em;
        margin: 1em auto;
      }
      
      .features-list {
        text-align: left;
        margin: auto;
      }
      

      This adds a border around the sales pitch and features list, then adds spacing between the elements to increase readability.

      Save and close layout.css.

      Lastly, you will render this component on the landing page. Open index.tsx in the src/pages/ directory. Add the <SalesPitchAndFeatures/> component to the rendered layout children:

      bookstore-landing-page/src/pages/index.tsx

      import * as React from "react"
      import { StaticImage } from "gatsby-plugin-image"
      
      import Layout from "../components/layout"
      import SalesPitchAndFeatures from "../components/salesPitchAndFeatures"
      import SEO from "../components/seo"
      
      const IndexPage = () => (
        <Layout>
          <SEO title="Home" />
          <div style={{ maxWidth: `450px`, margin: ' 1em auto'}}>
            <StaticImage
              src="https://www.digitalocean.com/community/tutorials/images/bookshelf.png"
              alt="Bookshelf hero image"
            />
            <SalesPitchAndFeatures/>
          </div>
        </Layout>
      )
      
      export default IndexPage
      

      You also added in a div to apply some styling to both the image and the sales pitch.

      Save and exit from the file. Restart your development server and you will find your sales pitch and features list rendered below your image:

      Rendered page with sales pitch and features added

      You now have a sales pitch on your landing page, which will help communicate to potential customers why they should go to your business. Next, you will build the final component for the landing page: an email signup form.

      Step 4 — Creating a Signup Form Component

      An email signup button is a common landing page component that lets the user enter their email address and sign up for more news and information about the product or business. Adding this to your landing page will give the user an actionable step they can take to become your customer.

      To start, create a new file in bookstore-landing-page/src/components called signupForm.tsx. This component won’t have any custom types but will have an event handler, which has its own special React-based type.

      First, build the <SignUpForm/> component and its return statement, with a header inside:

      bookstore-landing-page/src/components/signupForm.tsx

       import * as React from "react"
      
      const SignUpForm = () => {
        return (
          <h3>Sign up for our newsletter!</h3>
        )
      }
      
      export default SignupForm
      

      Next, add some markup to create a form element with an onSubmit attribute, initialized to null for now. Soon this will contain the event handler, but for now, finish writing the form with label, input, and button tags:

      bookstore-landing-page/src/components/signupForm.tsx

      import * as React from "react"
      
      const SignUpForm = () => {
        return (
          <React.Fragment>
            <h3>Sign up for our newsletter!</h3>
            <form onSubmit={null}>
              <div style={{display: 'flex'}}>
                  <input type="email" placeholder="email@here"/>
      
              <button type="submit">Submit</button>
              </div>
            </form>
          <React.Fragment>
        )
      }
      
      export default SignupForm
      

      If you type your email address in the text field and click Sign Up on the rendered landing page right now, nothing will happen. This is because you still need to write an event handler that will trigger whenever the form is submitted. A best practice is to write a separate function outside the return statement for the event handler. This function usually has a special e object that represents the triggered event.

      Before writing the separate function, you will write the function in-line to figure out what the static type of the event object is, so that you can use that type later:

      bookstore-landing-page/src/components/signupForm.tsx

      ...
      
          return (
              <React.Fragment>
                  <h3>Sign up for our newsletter!</h3>
                  <form onSubmit={(e) => null}>
                      <div style={{display: 'flex'}}>
                          <input type="email" placeholder="email@here" style={{width: '100%'}}/>
                          <button type="submit">Submit</button>
                      </div>
                  </form>
              </React.Fragment>
          )
      ...
      }
      
      export default SignupForm
      

      If you are using a text editor like Visual Studio Code, hovering your cursor over the e parameter will use TypeScript’s IntelliSense to show you its expected type, which in this case is React.FormEvent<HTMLFormElement>.

      Now that you know what the expected type of your separate function will be, go ahead and use this to write a new, separate function called handleSubmit:

      bookstore-landing-page/src/components/signupForm.tsx

      import * as React from "react"
      
      const SignupForm = () => {
          const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
              e.preventDefault();
              alert(alert('The submit button was clicked! You're signed up!'))
          }
          return (
              <React.Fragment>
                  <h3>Sign up for our newsletter!</h3>
                  <form onSubmit={handleSubmit}>
                      <div style={{display: 'flex'}}>
                        <input type="email" placeholder="email@here"/>
                        <button type="submit">Submit</button>
                      </div>
                  </form>
              </React.Fragment>
        )
      }
      
      export default SignupForm
      

      The handleSubmit function will now trigger a browser alert when the form is submitted. Note that this is a temporary placeholder; to actually add the user to an email list, you would have to connect this to a back-end database, which is beyond the scope of this tutorial.

      Save and close the signupForm.tsx file.

      Now, open the index.tsx file and add the new <SignupForm/> component:

      bookstore-landing-page/src/pages/index.tsx

      import * as React from "react"
      import { StaticImage } from "gatsby-plugin-image"
      
      import Layout from "../components/layout"
      import SalesPitchAndFeatures from "../components/salesPitchAndFeatures"
      import SignupForm from "../components/signupForm"
      import Seo from "../components/seo"
      
      const IndexPage = () => (
        <Layout>
          <Seo title="Home" />
          <div style={{ maxWidth: `450px`, margin: ' 1em auto'}}>
            <HeroImage />
            <SalesPitchAndFeatures />
            <SignupForm />
          </div>
        </Layout>
      )
      
      export default IndexPage
      

      Save and exit the file.

      Restart your development server, and you will find your completed landing page rendered in your browser, along with the email signup button:

      Email signup button rendered below the feature list on the landing page.

      You have now finished building all the core components of your bookstore landing page.

      Conclusion

      Because Gatsby creates fast static websites and TypeScript allows for data to be statically typed, building a landing page makes for an excellent use case. You can shape the types of its common elements (header, hero image, email signup, etc.) so that incorrect forms of data will trigger errors before they go into production. Gatsby provides the bulk of the structure and styling of the page, allowing you to build on top of it. You can use this knowledge to build other landing pages to promote other products and services quicker and more efficiently.

      If you’d like to learn more about TypeScript, check out our How To Code in TypeScript series, or try our How To Create Static Web Sites with Gatsby.js series to learn more about Gatsby.



      Source link