Skip to content

How do I correctly use ReactJS Reach Router with TypeScript & ESLint?

I’m trying to implement client-only routes using Reach Router in a project using TypeScript & ESLint. This is my initial implementation:

// src/pages/app/[...].tsx
import { Router, Redirect, useLocation } from '@reach/router';
import * as React from 'react';

import BrowseMain from '../../components/BrowseMain';
import Layout from '../../components/Layout';
import UploadMain from '../../components/UploadMain';
import * as styles from '../../styles/[...].module.scss';

const IndexPage = () => {
  const currLocation = useLocation();

  return (
    <Layout>
      <main className={styles.indexMain}>
        <Router basepath="/app">
          {['/', '/browse', '/browse/'].map((path) => <BrowseMain key={path} path={path} />)}
          {['/upload', '/upload/'].map((path) => <UploadMain key={path} path={path} />)}
          {/* Redirect 404 */}
          <Redirect noThrow default from={currLocation.pathname} to="/" />
        </Router>
      </main>
    </Layout>
  );
};

export default IndexPage;

// src/components/BrowseMain.tsx
import * as React from 'react';

import '../styles/BrowseMain.module.scss';
import Main from './Main';

const BrowseMain = () => <Main>Browse</Main>;

export default BrowseMain;

// UploadMain is similar to BrowseMain, so they are facing the same issue here.

// src/components/Main.tsx
import * as React from 'react';

import '../styles/Main.module.scss';

type MainProps = {
  children: string,
};

const Main = ({ children }: MainProps) => <>{children}</>;

export default Main;

At this point, TypeScript threw the following error on the BrowseMain and UploadMain routes in [...].tsx:

TS2322: Type ‘{ key: string; path: string; }’ is not assignable to type ‘IntrinsicAttributes’. ¬†¬†Property ‘path’ does not exist on type ‘IntrinsicAttributes’.

Following Reach Router’s documentation for Usage with TypeScript, I made the following change:

import { RouteComponentProps } from '@reach/router';
import * as React from 'react';

import '../styles/BrowseMain.module.scss';
import Main from './Main';

const BrowseMain = (props: RouteComponentProps) => <Main>Browse</Main>;

export default BrowseMain;

This solves the previous linting error, however two new ones are raised:

TS6133: ‘props’ is declared but its value is never read.

ESLint: ‘props’ is defined but never used.(@typescript-eslint/no-unused-vars)

At this point, I’m stuck. I tried two different things, but they both raised linting errors:

// ESLint: Unexpected empty object pattern.(no-empty-pattern)
const BrowseMain = ({}: RouteComponentProps) => (

// ESLint: '_' is defined but never used.(@typescript-eslint/no-unused-vars)
const BrowseMain = (_: RouteComponentProps) => (

I feel like I’m in a damned if you do, damned if you don’t situation right now. What’s the correct way to declare an explicit type for props that will not be used?

Answer

You can specify the props using generics. In your case it will look like:

const BrowserMain: React.FC<RouteComponentProps> = () => <Main>Browse</Main

Using React.FC is however discouraged so you may also do it with a typescript call signature

type BrowserMainFC = {
  (props: RouteComponentProps): JSX.Element
}

const BrowseMain: BrowserMainFC = () => <div />

Check out this resource: https://react-typescript-cheatsheet.netlify.app/docs/basic/getting-started/function_components/