Kategorier
Code Frontend

Minified React error in production with Vite.js

At work I inherited a really old website running on React.

The code was good but by old i mean really old; last commit was 3 years ago and most of the code was 5-6 years old…

So I converted it to TypeScript and ripped out the old babel/webpack build system and installed Vite to handle all the builds.

Everything went pretty smoothly and I got the site up and running with no errors in development mode.

When going to production is when things went sideways. Some pages crashed with the following error:

error: Minified React error #130; visit https://reactjs.org/docs/error-decoder.html?invariant=130&args[]=object&args[]= for the full message or use the non-minified dev environment for full errors and additional helpful warnings.
    at vc (vendor.93c27df6.js:23)
    at d (vendor.93c27df6.js:23)
    at vendor.93c27df6.js:23
    at vendor.93c27df6.js:23
    at Ts (vendor.93c27df6.js:23)
    at ul (vendor.93c27df6.js:23)
    at ic (vendor.93c27df6.js:23)
    at tc (vendor.93c27df6.js:23)
    at ql (vendor.93c27df6.js:23)
    at zl (vendor.93c27df6.js:23)

Some quick googling led me to a GitHub issue showing me that I was not alone with this bug.

Package problem number one

It seems that esbuild is checking for __esModule at runtime and rollup (which is used internally in Vite) is not.

So it will happen to any library that has an entry point with: module.exports = require("./module");

Since a file exported this way does not include the ’magic string’ __esModule as checked by generate-exports.js it’s not converted correctly.

In my case I had two librarys acting up; react-datetime which was imported locally like this: import DateTime from "react-datetime"; which worked fine in Vite dev but not production.

Props to ntkoopman for the explanation.

Solution

User Haikyuu suggested using the following solution which is what I used in the end.

import DT from "react-datetime";
// @ts-ignore
const DateTime = DT.default ? DT.default : DT;

Package problem number two

The second package with this issue was react-dropzone which in turn used the real culprit; attr-accept

Since the project used an old version (4.x.x, latest version is at the time of writing 14.x.x) and I got other issues upgrading to the latest version I could rule out a PR from me fixing it.

Solution number two

Instead I used patch-package and postinstall to create a patch-file with the fix.

//from
import accepts from 'attr-accept';

//to
import A from "attr-accept";
const accepts = A.default ? A.default : A;

I how this can help if someone is stuck, and I hope it will be fixed soon because it is kind of a blocker for using Vite.js in production.

Kategorier
Code

Quick tip: .env in next.js

Using environment variables is a great way to store environment specific values and secrets that should not be in the git repo.

Lets say you have an API which runs on localhost locally but a different host in production.

On the server

Locally you need to add an .env.local-file in your project root containing a variable for the api host name, lets call it API_HOST and point it to localhost port 8080.

# .env.local in project root
API_HOST=http://localhost:8080

You would then need to add the environment variable to your production env as well. This will look different depending on your hosting provider but on Google Cloud Run it looks like this:

On the client

To expose your environment values to the client, you need to add them to your next.config.js

In most cases keeping using the variable on the server is enough, but in some cases you might need to access the environment variable on the client as well and in those cases you need to add the variable to your next.config.

// next.config.js
module.exports = {
  env: {
    API_HOST: process.env.API_HOST
  }
}
Kategorier
Code Frontend

Great HTML5 tags you may have missed

Since front end development nowadays is so focused on JavaScript it’s easy to forget that new features are added to HTML as well.

HTML 5.1 is already 5 years old (1 November 2016) and I am embarrassed to say that there are some new tags that have passed me by.

<summary> and <details>

Ever since the golden (?) days of jQuery developers have been forced to use JavaScript to add collapsible fields to a page, but no more!

The <summary> tag is used with <details> to specify a visible heading with a (optionally) hidden, collapsible content field.

Code:

<details>
  <summary>Read more...</summary>
  Details are hidden from view 😮
</details>

Result:

Read more… Details are hidden from view 😮

It’s also possible to have the details text open by default, just add an open-property.

Code:

<details open>
  <summary>Read more...</summary>
  This text is not hidden by default
</details>

Result:

Read more… This text is not hidden by default

This property can also be used to change the styling when open/closed and it’s good to know that the arrow can be changed or removed as well.

Code:

details {
  background-color: #fff;
}
details > summary {
  list-style: none;
}
details[open] {
  background-color: #ccc;
}

Result:

Read more… This text is not hidden by default

If you can’t stay away from JavaScript, the <details>-tag has a toggle-event that you can subscribe to for updates.

<datalist>

The <datalist> tag specifies a list of pre-defined options and allows users to add more to it. It provides an autocomplete feature that allows you to get the desired options with a type-ahead.

It would be easy to think of this as an alternative to the <select>-tag, but it should be seen as a text-input with built in suggest functionality since the user is not forced to explicitly select one of the given values.

Code:

<label for="fav-lang">Favourite programming language:</label>
<input list="lang" id="fav-lang" name="fav-lang" />

<datalist id="lang">
    <option value="TS">TypeScript</option>
    <option value="JS">JavaScript</option>
    <option value="Java">
    <option value="C#">
    <option value="Rust">
    <option value="Python">
</datalist>

Result:

Caveats

There seem to be some issues when using datalist with a large list (> 512) of options so you might want to consider how many items you put in the list.

<meter>

Use the <meter> tag to visualize a value in a range of numbers. The most common use case I’ve seen is for visualizing the strength in a password.

Code:

<label for="score">Lighthouse score:</label>
<meter id="score"
       min="0" max="100"
       low="40" high="80" optimum="90"
       value="85">85/100
</meter>

Result:

85/100

<progress>

Quite similar to the <meter>-tag but, progress always has a minimum value of 0.

Code:

<label for="progress">Progress:</label>
<progress id="progress" max="5" value="3">3/5</progress>

Result:

3/5

<mark>

The <mark>-tag is used to semantically highlight any text for clarity and is maybe best used in search results to mark parts of a bigger text block.

Code:

<p>“Ph’nglui mglw’nafh Cthulhu R’lyeh wgah’nagl fhtagn.”</p>
<p>Legrasse had one point in advance of Professor Webb, for several among his mongrel prisoners had repeated to him what older celebrants had told them the words meant. This text, as given, ran something like this:</p>
<p>“In his house at R’lyeh dead <mark>Cthulhu waits dreaming.</mark>”</p>
<p>And now, in response to a general and urgent demand, Inspector Legrasse related as fully as possible his experience with the swamp worshippers; telling a story to which I could see my uncle attached profound significance. It savoured of the wildest dreams of myth-maker and theosophist, and disclosed an astonishing degree of cosmic imagination among such half-castes and pariahs as might be least expected to possess it.</p>

Result:

“Ph’nglui mglw’nafh Cthulhu R’lyeh wgah’nagl fhtagn.”

Legrasse had one point in advance of Professor Webb, for several among his mongrel prisoners had repeated to him what older celebrants had told them the words meant. This text, as given, ran something like this:

“In his house at R’lyeh dead Cthulhu waits dreaming.

And now, in response to a general and urgent demand, Inspector Legrasse related as fully as possible his experience with the swamp worshippers; telling a story to which I could see my uncle attached profound significance. It savoured of the wildest dreams of myth-maker and theosophist, and disclosed an astonishing degree of cosmic imagination among such half-castes and pariahs as might be least expected to possess it.

Autocapitalize <input>

While not a tag, the autocapitalize attribute can be added to input-elements to force digital keyboards, usually on mobile devices, to behave in a certain way.

It’s good to know that the attribute will not do any changes to the actual value for the input, so it will have no impact when used with a physical keyboard. autocapitalize will make the mobile keyboard change case depending on the value given to the attribute.

Code:

// none: all letter should default to lowercase
<input type="text" autocapitalize="none" />

// characters: ALL CHARACTERS SHOULD BE UPPERCASE
<input type="text" autocapitalize="characters" />

// characters: First letter of each sentence should be uppercase
<input type="text" autocapitalize="sentences" />

// characters: First Letter Of Every Word Should Be Uppercase
<input type="text" autocapitalize="words" />

It should be noted that an input of type urlemail, or password will not use autocapitalize.

Kategorier
Code Frontend

CSS modules in next.js

On bergqvist.it I used styled jsx for styling my components. I preferred that to other css-in-js frameworks (like JSS) because of it actually using CSS syntax instead of JavaScript objects.

// styled jsx example with good old CSS
<style jsx>{`
  .label { color: red; font-style: italic; }
  .article { padding: 0; }
`}
</style>

//JSS example with CSS as a JS object
const useStyles = createUseStyles({
  label: {
    color: 'red'
    fontStyle: 'italic'
  },
  article: {
    padding: 0
  }
})

I like Styled jsx but it has had an issue with FOUC in the last few versions of Next.js though, and with Next 12 I decided to try something new and migrate to CSS Modules instead.

What is CSS Modules?

CSS Module is a CSS file in which all class names and animation names are scoped locally by default. 

So the benefit are the same as css-in-js-frameworks but pure css (or in my case scss) files are used instead of keeping the styling in the javascript files.

Read more about CSS Modules here

Why CSS Modules?

I may be old school, but I actually prefer to keep the CSS and JavaScript separated for each other. I can see the benefits of keeping them tightly coupled and can agree that simple, self-contained components probably benefit from this approach, but it gets messy when the component has many styles with media queries.

I also want to use SASS, which is fully supported with CSS Modules in Next.js.

Migrating from styled jsx to CSS Modules

Since Styled jsx use regular CSS it’s actually just a matter of creating the <component>.module.scss-file, importing it into the component and changing the classes

//styled jsx
export default function MyComponent(){
  return (<div className={"article"}>
    <span className={"label"}>...</span>
    <style jsx>{`
      .label { color: red; font-style: italic; }
      .article { padding: 0; }
    `}</style>
  </div>)
}

//CSS Modules
import styles from "./MyComponent.module.scss";

export default function MyComponent(){
  return (<div className={styles.article}>
    <span className={styles.label}>...</span>
  </div>)
}

Using multiple modules in one component

For reusability you might want to use a css module in more than one component


import styles from "./MyComponent.module.scss";
import * as secondaryStyles from "./secondary.module.scss";

export default function MyComponent(){
  return (<div className={styles.article}>
    <span className={secondaryStyles.label}>...</span>
  </div>)
}

If you are using TypeScript this approach probably cause an error: TS2339: Property 'label' does not exist on type 'typeof import("*.module.scss")'.

The error can be can be mitigated by adding a typings.d.ts -file to the root of your project with the following content

// typings.d.ts
declare module "*.module.scss" {
  interface IClassNames {
    [className: string]: string;
  }
  const classNames: IClassNames;
  export = classNames;
}

Composition

Instead of importing several different modules it’s possible to compose new classes from existing classes.

// secondary.module.scss
.label { 
  color: red; 
  font-style: italic; 
}

// MyComponent.module.scss
.article { 
  padding: 0; 
}
.label {
  composes: label from "./secondary.module.scss";
}


// MyComponent.tsx
import styles from "./MyComponent.module.scss";

export default function MyComponent(){
  return (<div className={styles.article}>
    <span className={styles.label}>...</span>
  </div>)
}

Global styles

As I already had a global css-file that I imported into my _app.tsx, I really did not have to do anything to get my global classes working.
If you want to add a global class in a component file you can add it by using :global() on the class.

:global(.label) { 
  color: red; 
  font-style: italic; 
}

Parting words

I’m quite happy with CSS Modules, the site no longer gets FOUC and it looks great with JavaScript disabled as well!

Hope this could be of any help to someone looking into CSS Modules.

Kategorier
Code testing

Tips and tricks for testing with Jest

Writing tests can be daunting when starting out, it’s hard to know exactly what to test and then learning the API for your test tool.

I wanted to share some small tips that can be useful when starting out.

expect.objectContaining()

In some cases you are only interested in the value of one or just a few properties in an object. To check for a specific property you can use expect.objectContaining to check if the object contains a property with the expected value.

In the code below we’re checking if a report dialog function has been called with the users name and email.
The actual object is much larger but we don’t really care about the other properties, in this case the user information is the moving parts in the object.

expect(showReportDialog).toHaveBeenCalledWith(
  expect.objectContaining({
    user: {
      name,
      email,
    }
  })
);

expect.anything()

Callback functions or randomly generated values can sometimes be a hassle to handle in tests since they might change, but it is possible to ignore specific properties or arguments using expect.anything.

function loadScript(scriptUrl:string, callback:() => unknown) { ... }

When testing the above function we ’re not interested in the callback function and only wants to check if loadScript have been called with the correct script.

it("should call loadScript", () => {
  someFunctionUsingLoadScript();

  expect(loadScript).toHaveBeenCalledWith(
    "script.js",
    expect.anything()
  );
}

expect.anything does not match null or undefined values

expect.any()

Another way to match more broadly is expect.any(constructor) where you can accept any match based on the constructor being passed to it.

expect(someFn).toHaveBeenCalledWith({
  someNumber: expect.any(Number),
  someBoolean: expect.any(Boolean),
  someString: expect.any(String)
});

expect.assertions()

When doing asynchronous tests it can be helpful to make sure that all assertions have been run when the test ends.
The expect.assertions(Number) ensures that the correct number of assertions have been made.

test('prepareState prepares a valid state', () => {
  expect.assertions(1);
  prepareState((state) => {
    expect(validateState(state)).toBeTruthy();
  })
  return waitOnState();
})

test.each

For some unit tests you may want run the same test code with multiple values. A great way to do this is using the test.each function to avoid duplicating code.

Inside a template string we define all values, separated by line breaks, we want to use in the test. The first line is used as the variable name in the test code.

test.each`
  someId
  ${undefined}
  ${null}
  ${""}
`("$someId should reject promise", async ({ someId}) => {
  expect.assertions(1);
  await expect(someFn(someId))
    .rejects.toEqual(errorMessage);
});

Multiple input variables can be added separated by the pipe (|) character.

test.each`
  someId       | anotherValue
  ${undefined} | ${a}
  ${null}      | ${b}
  ${""}        | ${c}
`("$someId with $anotherValue should reject promise", async ({ someId, anotherValue }) => {
  expect.assertions(1);
  await expect(someFn(someId, anotherValue))
    .rejects.toEqual(errorMessage);
});

Note: it is also possible to define the values as arrays, read more in the official documentation.

jest.requireActual

This is just a reminder never to forget adding jest.requireActual when mocking libraries. If you do forget, it can lead to weirdness that may take several hours to solve (talking from personal experience here 😁).

So what does it do?

When mocking a library you may only want to mock a specific function of the library and keep the rest of the library intact.

jest.mock("@material-ui/core", () => ({
  ...jest.requireActual("@material-ui/core"),
  useMediaQuery: jest.fn()
}));

So in the code above we create a new mock object, using jest.requireActual to spread all the functions of the library and only mock useMediaQuery in this case.

Kategorier
Code Frontend

Extending the theme in Material UI with TypeScript

When we started using Material UI (version 3) the support for extending the built-in theme was pretty lacking. The theme interface did not handle any additional color settings such as ”success” or ”warn” and trying to extend the theme did not work since parts of interfaces can’t be overwritten.

So instead of extending the theme we used a separate object with corresponding interface to handle the extra colors that we needed. Not ideal but as the colors only were used in a few places we could afford to wait for the support in MUI to get better.

Flash forward a year and the support is here so extend the theme we did!
The documentation tells us to use module augmentation to merge our theme with the built-in theme by creating a index.d.ts file and adding our properties in that.

The official way of doing it

So if I want to extend the typography object to accept a secondaryFontFamily I would have to do something like this:

declare module "@material-ui/core/styles/createTypography" {
  interface TypographyOptions {
    secondaryFontFamily: string;
  }
  interface Typography {
    secondaryFontFamily: string;
  }
}

And then creating a custom theme factory function to create the theme.

import { createMuiTheme} from '@material-ui/core/styles';

export const createMyTheme():CustomTheme => createMuiTheme({
  palette: createPalette({}),
  typography: {
    secondaryFontFamily: "Georgia"
  }
});

This works well but still uses the Theme interface which makes it harder to know what has been extended.

Our project setup

We package our code in different NPM packages and use Lerna to handle the development environment.

That means that the theme is used over several packages and when we implemented the solution above we quickly realized that we had to add the index.d.ts file in every project, making it very cumbersome to add new attributes in the future.

Back to the drawing board.

A different solution

So we need an interface for our customised theme that we can share with our packages.

import React from "react";
export interface CustomTypography extends Typography {
  secondaryFontFamily: string;
}
export interface CustomTheme extends Theme {
  typography: CustomTypography;
}

export const createMyTheme():CustomTheme => createMuiTheme({
  palette: createPalette({}),
  typography: {
    secondaryFontFamily: "Georgia"
  }
});

That will unfortunately result in the following error:
Image of code with a typescript error: S2345: Argument of type '{ palette: Palette; typography: { secondaryFontFamily: string; }; }' is not assignable to parameter of type 'ThemeOptions'. Types of property 'typography' are incompatible.

TypeScript does not allow interfaces to be merged and since CustomTheme extends Theme it seemed that we are out of luck.

Then I discovered Omit.

TypeScript Omit to the rescue!

Omit<T,K> is an utility type that constructs a type by picking all properties from T and then removing K.

So by using Omit we can create our own utility type Modify. (Props to Qwerty )

type Modify<T, R> = Omit<T, keyof R> & R;

Which will merge two interfaces, removing any members on T that exists in R and then adding R to the resulting type.

So using Modify we can do this instead:

import { Theme } from "@material-ui/core";
import { Typography } from "@material-ui/core/styles/createTypography";

export type CustomTypography = Modify<
  Typography,
  {
    secondaryFontFamily: string;
  }
>;

export type CustomTheme = Modify<
  Theme,
  {
    typography: CustomTypography;
  }
>;


export const createMyTheme():CustomTheme => {
  const baseTheme = createMuiTheme({
    palette: createPalette({})
  });

  return {
    ...baseTheme,
    typography: {
      secondaryFontFamily: "Georgia"
    }
  }
});

And use it in our app like this:

const MyApp = () => {
const myTheme = createMyTheme();
  return (
    <ThemeProvider<CustomTheme> theme={myTheme}>
      <CssBaseline />
        <SomeComponent />
    </ThemeProvider>
  );
};

I hope this can help get someone with the same problem some ideas and if you have solved the problem in another way, please let me know.

Kategorier
Frontend

How to mock the Redux useSelector hook

There is an official way of using RTL with redux as some people pointed out, but for small quick tests mocking useSelector may still be of use. 🙄

Recently I finally made the switch from Enzyme to React testing library (RTL) which also means that instead of rendering components using shallow like Enzyme proposes, with React testing library the whole component and its child components is rendered, much like Enzymes mount.

The switch to RTL coupled with using hooks instead of HOCs when using Redux got me writing a lot of new component tests but I did run in to some problem when I tried to use the useSelector-hook from Redux multiple times expecting different responses.

The component that I wanted to test as a search component that made calls similar to this:

const MySearchComponent = () => {
  const { query, rows } = useSelector((state) => 
    state.config);

  const {
      items,
      hasMore
    } = useSelector((state) => state.search);

  return (...)
}

useSelector takes a callback function that takes the state as an argument and returns a slice of the state.

So my first approach when trying to test the component was to send two different responses.

jest.mock("react-redux", () => ({
  useSelector: jest.fn()
    .mockReturnValueOnce(mockConfigState)
    .mockReturnValueOnce(mockSearchState)
}));

describe("MySearchComponent", () => {
  afterEach(() => {
    useSelector.mockClear();
  });
  it("should render", () => {
    const { getByTestId } = render(<MySearchComponent />);
    expect(...)
  });
});

Which worked fine until I realised that a child component also calls useSelector and therefore crashed. 😱

I knew I needed something that would support all possible selectors that I needed but still could be modified on a test by test basis.
I had a mock state ready, but not the method to alter and inject it.
Until I ran across jest.fn().mockImplementation

The solution to my problems

useSelector takes a callback as its argument and all I had to do was to call that callback with a compatible state that would satisfy all my components needs and they would do the rest as implemented.

jest.mock("react-redux", () => ({
  useSelector: jest.fn()
}));

describe("MySearchComponent", () => {
  beforeEach(() => {
    useSelector.mockImplementation(callback => {
      return callback(mockAppState);
    });
  });
  afterEach(() => {
    useSelector.mockClear();
  });
  it("should render a query", () => {
    const { getByTestId } = render(<MySearchComponent />);
    expect(getByTestId("query_testId").textContent)
      .toEqual(mockAppState.config.query)
  });
  it("should not render if query is empty", () => {
      const localMockState = {
        ...mockAppState,
        config: {
          ...mockShoppingState.config,
          query: ""
        }
      };
      useSelector.mockImplementation(callback => {
        return callback(localState);
      });
    const { queryByTestId } = render(<MySearchComponent />);
    expect(queryByTestId("query_testId")).toBeNull();
  });
});

So in the code above I mock useSelector from the react-redux npm package and replaces it with a function that executes any given callback function with my mocked state as an argument. This is done before every test.

In the second test I create a second mocked state that I want to use for just that test so I override useSelector to make sure it uses my updated state instead of the default mock state.

Parting words

I hope this helped someone to learn a little bit more about how to test their code and what can be achieved with jest and tools like RTL (which is great, try it!)

All typos my own and please leave a comment if you have a question or something does not make sense.

Kategorier
Frontend

How to add an RSS-feed to your Next.js site

I love RSS-feeds (and still curse Google for cancelling Google Reader) and use them as my main news source for things I find interesting so with this article I would like to help people to add RSS-feeds to their blogs.

If you read my article about how to add a sitemap.xml to your next.js site you will recognise much of the code, it’s pretty much the same base but with slightly different XML-markup.

Creating the page

First off we need a page that can return the XML. I suggest you name it ”rss”, ”feed” or something similar.

In getInitialProps we get our blog posts and set the ”Content-Type”-header to ”text/xml” so the browser knows that should expect XML instead of HTML.
We then generate the XML and passes it on to the browser using res.write.

export default class Rss extends React.Component {
  static async getInitialProps({ res }: NextPageContext) {
    if (!res) {
      return;
    }
    const blogPosts = getRssBlogPosts();
    res.setHeader("Content-Type", "text/xml");
    res.write(getRssXml(blogPosts));
    res.end();
  }
}

Generating the base XML for the feed

For the base XML document you will need to add some basic information like the title of the log, a short description, link to your website and the language of the content.
Title, link and description are mandatory elements in the RSS specification but add as many optional elements as you find useful.

const getRssXml = (blogPosts: IBlogPost[]) => {
  const { rssItemsXml, latestPostDate } = blogPostsRssXml(blogPosts);
  return `<?xml version="1.0" ?>
  <rss version="2.0">
    <channel>
        <title>Blog by Fredrik Bergqvist</title>
        <link>https://www.bergqvist.it</link>
        <description>${shortSiteDescription}</description>
        <language>en</language>
        <lastBuildDate>${latestPostDate}</lastBuildDate>
        ${rssItemsXml}
    </channel>
  </rss>`;
};

Adding XML for the blog items

With the basic stuff out of the way all we need to go is generate some XML for the blog posts and figure out when the blog was updated.

The item element should at least contain a link to the blog post, the date when it was published and the text. You can either opt to put a short description and force the user to come visit your page or put the whole post in the XML.

If you have HTML-markup in your text you need to enclose it within a <![CDATA[${post.text}]]>-tag or HTML encode the text.

const blogPostsRssXml = (blogPosts: IBlogPost[]) => {
  let latestPostDate: string = "";
  let rssItemsXml = "";
  blogPosts.forEach(post => {
    const postDate = Date.parse(post.createdAt);
    if (!latestPostDate || postDate > Date.parse(latestPostDate)) {
      latestPostDate = post.createdAt;
    }
    rssItemsXml += `
      <item>
        <title>${post.title}</title>
        <link>
          ${post.href}
        </link>

        <pubDate>${post.createdAt}</pubDate>
        <description>
        <![CDATA[${post.text}]] >
        </description>
    </item>`;
  });
  return {
    rssItemsXml,
    latestPostDate
  };
};

Finishing up

The last thing you need to do is to add a link to your feed somewhere in the head of your document.

<link 
  rel="alternate" 
  type="application/rss+xml" 
  title="RSS for blog posts" 
  href="https://www.bergqvist.it/rss" />

This will make it easier for browsers and plugins to auto discover your feed.

The code is available as a gist on github and please leave a comment with any feedback.

Kategorier
Frontend

Adding a dynamic sitemap.xml to a next.js site

While building my blog with Next.js I naturally wanted to support sitemaps because it can supposedly help out the search engines. For my small blog it will not make any difference but for larger sites it’s more important. Google has this to say.

Using a sitemap doesn’t guarantee that all the items in your sitemap will be crawled and indexed, as Google processes rely on complex algorithms to schedule crawling. However, in most cases, your site will benefit from having a sitemap, and you’ll never be penalized for having one.

Page for the sitemap

The first thing we need to do is to create a sitemap.xml.ts page in the ”page”-folder. This will expose a https://yourdomain.com/sitemap.xml url that you can submit to search engines. if you want to you can omit the .xml part and only use /sitemap, Google search console will accept the url anyway.

We want to make sure that we set the Content-Type header to text/xml and to write our xml output to the response stream.

export default class Sitemap extends React.Component {
  static async getInitialProps({ res }: any) {
    const blogPosts = getBlogPosts();
    res.setHeader("Content-Type", "text/xml");
    res.write(sitemapXml(blogPosts));
    res.end();
  }
}

Generating the base xml

For the site map we want to list all pages on the site, apart from the blog posts we have to add all additional pages that we want the search engines to find.

I have an about page that I add manually together with the index page but if you have many pages I suggest you create an array and add them in a more automated way.

I won’t go into the inner workings of all the properties of a sitemap but I want to mention the <priority>-tag that lets you set a value between 0 and 1 indicating how important you think the page is.
<lastmod> is used to indicate when the page was changed.

const sitemapXml = (blogPosts: IBlogPostListItem[]) => {
  const { postsXml, latestPost } = blogPostsXml(blogPosts);
  return `
  <?xml version="1.0" encoding="UTF-8"?>
    <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
    <url>
      <loc>https://www.bergqvist.it/</loc>
      <lastmod>${latestPost}</lastmod>
      <priority>1.00</priority>
    </url>
    <url>
      <loc>https://www.bergqvist.it/about</loc>
      <priority>0.5</priority>
    </url>
    ${blogPostsXml}
  </urlset>`;
};

Adding xml for the blog posts

As mentioned above I want to add the dynamic blog post pages to the site map as well. In the blogPostsXml-function I generate xml for all posts and keep track of when the latest post was posted.

const blogPostsXml = (blogPosts: IBlogPostListItem[]) => {
  let latestPost = 0;
  let postsXml = "";
  blogPosts.map(post => {
    const postDate = Date.parse(post.createdAt);
    if (!latestPost || postDate > latestPost) {
      latestPost = postDate;
    }
    postsXml += `
    <url>
      <loc>${post.url}</loc>
      <lastmod>${postDate}</lastmod>
      <priority>0.80</priority>
    </url>`;
  });
  return {
    postsXml,
    latestPost
  };
};

Last words

Make sure to check that you do not add any pages to the sitemap that is blocked in your robots.txt.

If you have a large site with many pages (100 or more) you can split up the sitemap into several smaller ones, for very large sites this is a must!

I hope this could help out someone and please leave a comment. A full gist of the code can be found here

Kategorier
conference

SmashingConf New York 2019

A few weeks ago I had the privilege of attending SmashingConf in New York, travelling from a chilly Swedish autumn to a quite pleasant New York end of summer. I spent the weekend fighting jet lag by taking long walks and touristing so I could be fit for the first workshop I had signed up for.

TL;DR

Great conference but not technical enough for my taste.

Watch Wes Bos’s talk (slides) for good javascript insights and all talks can be found here: SmashingConf NY 2019 videos.

Lightning fast performance with Scott Jehl.

Spending a whole day digging deep into performance sounded like a great way to spend a Monday so I had booked.
The venue for the workshop was located at Microsoft, just a short walk from my hotel so I had plenty of time to get a good nights sleep, eat a steady breakfast and still make the 9 o’clock start without any problems.

Scott Jehl was really easy going and the workshop covered tools to measure performance and how to mitigate issues.
I would however have liked more nitty gritty details and less time spent on using online tools.

The Move Fast & Don’t Break Things-talk that Scott held on the second day of the conference was a distilled version of the workshop so check it out if you’re interested.

In the evening I attended a Shopify-hosted jam session with lightning talks from Shopify, Microsoft, Forestry (about TinaCMS!) and a few others talkers.

Conference day one

The conference took place at the New world stages close to time square.
The main stage was a musical theatre which worked well but the rest of the venue was quite cramped.

Many of the talks were more of the UX/inspirational kind which can be nice, but I’m more of a hands on person so my favourites were the following.

Marcy Sutton on Garbage Pail Components is a talk about accessibility with an 80’s twist (and not even the first 80’s flashback this conference).

Wes Bos on Slam Dunk Your JavaScript Fundamentals is jam packed with new APIs that you might not have heard of yet, especially the Intl-object.

In the evening the SmashingConf party took place at Waylon bar where speakers and participants got to mingle. I met lots of friendly people from both sides of the pond and had a great time.

Conference day two

With a slight hangover and not enough sleep I went to see the mystery speaker dina Amin who makes wonderful stop-motion shorts with old discarded electrical devices.

Another fun talk was the Using a Modern Web to Recreate 1980s Horribly Slow & Loud Loading Screen-talk where Remy Sharp recreated the loading screens of his old 80’s Sinclair Spectrum computer. All ending with a photo of the audience being transformed to the Spectrum image format and shown on the big screen

Next.js with Remy Sharp

With the conference over I still had a workshop day to look forward to so I went back to the Microsoft office to learn some next.js!

The workshop was good with all the basics of next.js covered, I had already tinkered with next.js (my homepage runs for example) so most things were familiar but luckily I had not read up on the changes in 9.1 so I did learn some new tricks.

All in all I had a great week in NYC and I met lots of great people but I would have liked if the conference and workshops were a bit more technical.