Use Typescript React component library with many props

Hi,

I’m creating a web & native React library about a responsive UI methodology called Every Layout.

First, I coded it in Typescript, then I discovered ReScript :heart_eyes:. I’m converting it.

I need to use the npm package @tamagui/core. The problem is its Typescript components have many of types, the each type have dozens of parameters (CSS and React Native). An example below.

My component Cluster.tsx component needs: type StackProps and component:

import React from 'react';
import { Stack, StackProps, Text } from '@tamagui/core';
import { toDimensionValue } from './utils';

export type CenterProps = StackProps & {
  maxWidth?: string | number;
  centerText?: boolean;
  gutters?: string | number;
  intrinsic?: boolean;
};

/**
 * The Center component is used for horizontally centering a block-level element,
 * with a max-width value representing the typographic measure.
 *
 * More on Every Layout Center.
 */
export const Center: React.FC<CenterProps> = ({
  children,
  maxWidth,
  centerText = false,
  gutters,
  intrinsic = false,
  ...props
}) => {
  return (
    <Stack
      maxWidth={toDimensionValue(maxWidth)}
      marginHorizontal="auto"
      paddingHorizontal={toDimensionValue(gutters)}
      alignItems={intrinsic ? 'center' : undefined}
      flexDirection={intrinsic ? 'column' : undefined}
      {...props}
    >
      {centerText ? <Text textAlign="center">{children}</Text> : children}
    </Stack>
  );
};

But I have problems in ReScript. I struggle to bind @tamagui/core to ReScript, as you can see in Tamagui.res github(dot)com/guillempuche/every-layout-react/blob/main/packages/react-tamagui/src/bindings/Tamagui.res and Cluster.res:

@module("@tamagui/core")
external // external stack: React.component<{..}> = "Stack"
stack: React.component<'props> = ({..}) => React.component<'props> = "Stack"
@module("@tamagui/core") external stack: React.element = "Stack"

@module("@tamagui/web")
external stack: ()

@module("@tamagui/core")
external text: React.component<{..}> = "Text"

module Stack = {
  @module("@tamagui/core") @react.component external make: {..} => React.element = "Stack"
}

Here you can have a look at the complex @tamagui/core code:

import { validStyles } from '@tamagui/helpers'

import { stackDefaultStyles } from '../constants/constants'
import { createComponent } from '../createComponent'
import type { StackProps, StackPropsBase, TamaguiElement } from '../types'

export type Stack = TamaguiElement

export const Stack = createComponent<StackProps, Stack, StackPropsBase>({
  acceptsClassName: true,
  defaultProps: stackDefaultStyles,
  validStyles,
})
  • StacksProps link: github(dot)com/tamagui/tamagui/blob/master/packages/web/types/types.d.ts#L754 type
export type StackStyleProps =
  WithThemeShorthandsPseudosMediaAnimation<StackStylePropsBase>

export type StackPropsBase = StackNonStyleProps &
  WithThemeAndShorthands<StackStylePropsBase>

export type StackProps = StackNonStyleProps & StackStyleProps

How can I bind Javascript/Typescript that can have dozens of paramaters? Do I need to convert them entiretly? And the fastest way?

Thanks :pray:

1 Like

Version 11 ( will make this easier. It adds cool new features like spread for record types:
https://rescript-lang.org/docs/manual/latest/record#record-type-spread

This is not directly equivalent to & in TypeScript types, but it should let you model them more easily.

1 Like

I reframe my question.

How can I import component <Stack> and <Text> Stack & Text — Tamagui Core that have dozens of properties?

Example of usage:
File Center.res

@react.component
let make = (~children: React.element, ~maxWidth=?, ~centerText=?, ~gutters=?, ~intrinsic=?) => {
  <stack
    maxWidth=maxWidthValue
    marginHorizontal="auto"
    paddingHorizontal={gutters}
    alignItems={intrinsic ? Some(#center) : None}
    flexDirection=flexDirectionValue>
    {centerText ? <text textAlign={#center}> children </text> : children}
  </stack>
}

Going to have to type these props yourself.

It’s recommended you only write bindings for what you use, don’t strive for complete type coverage at once. Write types/bindings for what you need as you go.

If there are many components with the same props, you can consider using shared props, which is a v12 feature.

You can see how I use shared props in these Framer Motion bindings

3 Likes

Great repo Victor! I’m learning a lot from your code.

Because I’m creating a UI library that can be used in Javascript projects, and they can pass many props ot my library, I skip Rescript for my UI library.

I’ll use Rescript in a non-library project that I’m also working on. I’ll apply your method, Victor :pray:.

rescript-mui is another good source for clever type sharing between components: https://github.com/cca-io/rescript-mui/blob/master/packages/rescript-mui-material/src/types/CommonProps.res

3 Likes

Yes! I need to blog about it after ReScript 11s release (and most of the docs are finished). Going forward we should have settled on more or less one way to do bindings.