Customer feedback is the most important driver of product decisions at ZEIT. Based on your feedback, we introduced now dev to simplify local development. Similarly, based on your suggestions, we are adding new regions to our Smart CDN every week.
To get even closer to our customers, today we are introducing a new way to reach out to us and we are telling you how we did it.

Real-time Engagement with Intercom

Intercom makes it easy for visitors to chat with online businesses in real-time. We are introducing Intercom on zeit.co, so that customers and visitors can share feedback and inquiries directly across our marketing pages, without changing pages.
As with all our core products, we strive to provide an optimal experience for everyone. This also applies to communication media. Some of our customers may prefer to not be distracted by messaging media, while others may not have access to consistently fast internet. To bring this feature to everyone, we thought through and implemented several fallbacks.

Custom Form for Edge Cases

To provide better accessibility, we identified and worked on supporting a number of edge cases. For example, a slow internet connection or an ad-blocker could interfere and prevent Intercom from loading correctly.
When zeit.co encounters a slow network connection, clicking Talk to Us button triggers a regular form within a modal. When connection resumes, interaction over the button enables our Intercom widget. This fallback solution also applies to Ad-blockers.

Lazy Loading Intercom

As described earlier, the primary interface uses Intercom for real-time messaging. We only load the Intercom module when a visitor hovers on Talk to Us on the navbar. This ensures that loading Intercom does not slow down the experience for visitors.
With Next.js Dynamic Import support, we lazy load the react-intercom module.
// intercom.js

import dynamic from 'next/dynamic'

const DynamicLoadedIntercom = dynamic(
  import('react-intercom')
  .then(comp => comp),
  {
    loading: function Loading() {
      return <span />
    },

    // Everything takes place on the client side
    ssr: false
  }
)


export default DynamicLoadedIntercom

React Hooks

To avoid dynamically mounting the Intercom component more than once, we store the preloaded info in a cache Map. With the help of React Hooks, we return the lazy-loaded component with inline styles for visibility.
// use-preload.js

import { useState } from 'react'

// The map stores all lazy-load components
const componentPreloaded = new Map()

const usePreload = (key, comp) => {
  const [preloaded, setPreloaded] = useState(componentPreloaded[key])
  const [show, setIsShown] = useState(false)

  const loadComp = () => {
    componentPreloaded[key] = true
    setPreloaded(true)
  }

  const showComp = () => {
    setIsShown(true)
  }

  let component = null

  if (isShow) {
    // The dynamic component will be visible
    component = { comp }
  }
  else if (preloaded) {
    // The dynamic component will be invisible, but preloaded
    component = <span style={{ display: 'none' }}>{comp}</span>
  } else {
    component = null
  }

  return { component, loadComp, showComp }
}

export default usePreload

Customize React Hook with reusable logic

Instead of a higher-order component where UI logic passes down as props, we use a custom hook to encapsulate behavior and return the desired component with reusable logic.
// use-talk-to-us.js

import FeedbackForm from '../components/feedback-form'
import usePreload from './lib/use-preload'

const useTalkToUs = () => {
  const [fallback, setFallback] = useState(false)

  // abstraction to preload/show the dynamic component
  const { component: intercomComp, loadComp, showComp } = usePreload(
    'intercom',
    <DynamicLoadedIntercom />
  )

  const showPopup = () => {
    showComp()
    if (window.intercom) {
      setFallback(false)
      window.intercom.show()
      // ... intercom related logic
    } else {
      setFallback(true)
      // failed to load intercom, show fallback component
    }
  }

  return {
    component: fallback ? <FeedbackForm /> : intercomComp,
    loadComp,
    showComp
  }
}

export default useTalkToUs
Now, we are able to apply the same interaction logic to any component. For instance, we can use it inside a button component:
import useTalkToUs from './lib/use-talk-to-us'

function TalkToUsButton () {
  const { component, loadComp, showComp } = useTalkToUs()

  return <>
    <button onHover={loadComp} onClick={showComp}>
      Talk To Us
    </button>
    {component}
  </>
}

export default TalkToUsButton

Conclusion

Along with our community chat, Twitter and support options, we are introducing Intercom as another prominent outlet for our customers to share feedback and make inquiries on zeit.co.
To help you set up real-time messaging on your website, we have created and open-sourced five examples demonstrating the most popular chat platforms, including:
All the code is available on a GitHub repository for you to try on your projects.
As always, for any questions or feedback, you can reach out to us via Twitter or Spectrum.