Performance

at FS

Tyler Graf

Web Dev

Tree

👨🏻

Topics

  1. per-locale
  2. faster pages

Per-locale

Issue

We used to load all locale strings on every page

zh

bg fr lt sk zh
cs hr lv sl zh-hans
da ht mk sm
de hu mn sq
el hy ms sr
en id nl sv
eo is no th
es it pl to
et ja pt tr
fi km ro uk
fj ko ru vi

@fs/zion-ui

en 1KB gzip

all

~45KB gzip

i18next refresher

import i18n from "i18next";
import {initReactI18next} from 'react-i18next'
import localeStrings from './allLocaleStrings.json'

i18n
  .use(initReactI18next)
  .init({
    lng: "zh",
    fallbackLng: "en",
    resources: localeStrings
  });
import React from 'react'
import { useTranslation } from "react-i18next";

export default function App(){
  const { t } = useTranslation();
  return <div>{t("title")}</div>
}
@fs/zion-locale/src/index.js
App.js
import i18n from "i18next";

export const addTranslations = (translations) => {
  translations.forEach(locale=>{
    i18n.addResources(locale, 'translation', translations[locale])
  })
}

// ... lots of other stuff too
import React from 'react'
import { addTranslations } from '@fs/zion-locale'
import { useTranslation } from "react-i18next";
import translations from './locales'

addTranslations(translations)

export default function App(){
  const { t } = useTranslation();
  return <div>{t("title")}</div>
}
@fs/zion-locale
App.js
import i18n from "i18next";
import {initReactI18next} from 'react-i18next'
import localeStrings from './allLocaleStrings.json'

i18n
  .use(initReactI18next)
  .init({
    lng: "zh",
    fallbackLng: "en",
    resources: localeStrings
  });
@fs/zion-locale/src/index.js
import i18n from "i18next";
import {initReactI18next} from 'react-i18next'
import enAppTranslations from '/coalesced-locales/dist/en/translation.json'

i18n
  .use(initReactI18next)
  .init({
    lng: "zh",
    fallbackLng: "en",
    resources: enAppTranslations
  });
import i18n from "i18next";
import {initReactI18next} from 'react-i18next'
import enAppTranslations from '/coalesced-locales/dist/en/translation.json'

i18n
  .use(initReactI18next)
  .init({
    lng: "zh",
    fallbackLng: "en",
    resources: enAppTranslations,
    backend: 
      resourcesToBackend((language, callback) => {
        import(`/coalesced-locales/dist/${language}.json`)
          .then(({ default: resources }) => {
            callback(null, resources)
          })
          .catch((error) => {
            callback(error, null)
          })
      }),
    },
  });
const {t} = useTranslation()

<div>{t('title')</div>
import React from 'react'
import { addTranslations } from '@fs/zion-locale'
import { useTranslation } from "react-i18next";
import translations from './locales'

addTranslations(translations)

export default function App(){
  const { t } = useTranslation();
  return <div>{t("title")}</div>
}
App.js
import React from 'react'
// import { addTranslations } from '@fs/zion-locale' remove me
import { useTranslation } from "react-i18next";
import './locales' // magic

// addTranslations(translations) remove me

export default function App(){
  const { t } = useTranslation();
  return <div>{t("title")}</div>
}

The magic bits

import './locales'

import enLocales from '/coalesced-locales/...'
$ npm start

Dependency Graf 😜

{
  // from App locales
  "title": "FamilySearch",
  "description": "Coolest place",
  
  // from shared component locales
  "label": "Birth",
  "value": "Stuff",
  
  // from other shared component locales
  "things": "Things",
  "all.of.the.awesome": "All of the awesome"
}

Coalesced Locales

src/locales/dist/en/translation.json
import enLocales from '/coalesced-locales/...'
import i18n from "i18next";
import {initReactI18next} from 'react-i18next'
import enAppTranslations from '/coalesced-locales/dist/en/translation.json'

i18n
  .use(initReactI18next)
  .init({
    lng: "zh",
    fallbackLng: "en",
    resources: enAppTranslations,
    backend: 
      resourcesToBackend((language, ns, callback) => {
        import(`/coalesced-locales/dist/${language}/${ns}.json`)
          .then(({ default: resources }) => {
            callback(null, resources)
          })
          .catch((error) => {
            callback(error, null)
          })
      }),
    },
  });

Perfomance Results

Weber-Fencher Law

30% Δ

before

after                                                                   8.6s

12.3s

(30% faster)

Load time (3.5G)

before

after                                             1009KB

1815KB

(44% smaller)

Payload

Temple Reservations

before

after                                                    7.7s

13.6s

(43% faster)

Load time (3.5G)

before

after                                               1517KB

2663KB

(43% smaller)

Payload

Search Collections

How can I make my app load faster?

  • Eliminate render-blocking resources
  • Properly size images
  • Defer offscreen images
  • Minify CSS
  • Minify JavaScript
  • Remove unused CSS
  • Efficiently encode images
  • Serve images in modern formats
  • Enable text compression
  • Preconnect to required origins
  • Reduce server response times (TTFB)
  • Avoid multiple page redirects
  • Preload key requests
  • Use video formats for animated content
  • Reduce the impact of third-party code
  • Avoid non-composited animations
  • Lazy load third-party resources with facades

Ship Less Code

Know your bundle

npm run analyzeBundle

Potential savings: ~100KB

Actual Savings: 178KB

import React, { Suspense, useState, lazy } from 'react'

const AddFindFlow = lazy(() => import('./AddFindFlow'))

export default function AddFindFlowButton() {
  const [isOpen, setIsOpen] = useState(false)

  return (
    <Suspense fallback="Loading...">
      <button onClick={() => setIsOpen(true)}>Add Person</button>

      {isOpen && <AddFindFlow />}
    </Suspense>
  )
}

sideEffects: false

Review

  • Duplicate packages
  • Unneeded packages
  • Ship less code
  • Lazy Load
  • sideEffects: false

Thanks guys

Made with Slides.com