The motion.custom component should accept type parameters for passing the custom component props.
Having the following base component:
import styled from '@emotion/styled'
import * as system from 'styled-system'
export type BoxProps = system.BackgroundColorProps &
system.BorderProps &
system.FlexboxProps &
system.LayoutProps &
system.OpacityProps &
system.SpaceProps
export default styled.div<BoxProps>`
${system.compose(
system.border,
system.color,
system.flexbox,
system.layout,
system.space
)};
box-sizing: border-box;
`
Wrapping the custom component with motion.custom:
import Box from 'components/box'
const Container = motion.custom(Box)
And using the motion wrapped component passing my custom component original props:
<Container
minHeight={viewportHeight}
display="flex"
flexDirection="column"
alignItems="center"
justifyContent="center"
opacity={viewportHeight === 0 ? 0 : 1}
p={2}
>
Test
</Container>
Gives the following TypeScript error:
Type '{ children: (string | number | boolean | {} | ReactElement<any, string | ((props: any) => ReactElement<any, string | ... | (new (props: any) => Component<any, any, any>)> | null) | (new (props: any) => Component<...>)> | ... 4 more ... | undefined)[]; ... 6 more ...; p: number; }' is not assignable to type 'IntrinsicAttributes & MotionProps & RefAttributes<Element>'.
Property 'children' does not exist on type 'IntrinsicAttributes & MotionProps & RefAttributes<Element>'.ts(2322)
So my solution is to just accept a generic type parameter in motion.custom to avoid this errors:
import Box, { BoxProps } from 'components/box'
const Container = motion.custom<BoxProps>(Box)
Ran into this issue too and I believe a good fix could be if the type ends up as this:
// framer-motion.d.ts — line: 1953
custom: <Props = {}>(Component: ComponentType<Props>) => ForwardRefExoticComponent<MotionProps & RefAttributes<Element> & Omit<ComponentProps<typeof Component>, keyof MotionProps> & { children: ReactNode }>;
Highlights:
This would need no further changes to the motion.custom API and would _just work_.
I'll try a PR later but have no time at the moment to debug why the bootstrap command is failing on my mac.
I think the line changes needed should be around here:
// motion/index.tsx — line: 64
export const motion = {
custom: (Component: ComponentType<any>) => {
return createMotionComponent(createDomMotionConfig(Component))
},
...htmlMotionComponents,
...svgMotionComponents,
}
//
// replace custom with this function:
//
function custom<P = {}>(Component: ComponentType<P>) { return createMotionComponent(createDomMotionConfig(Component)) }
Haven't tested it, but it must be along those lines.
In the meantime here's a utility function animated that implements it:
//
// Utility to create a type-safe motion.custom
//
// Example:
//
// const AnimatedBox = animated(Box)
//
export function animated<Props = {}>(Component: ComponentType<Props>) {
return motion.custom(Component) as ForwardRefExoticComponent<
MotionProps &
RefAttributes<Element> &
OmitMotionProps<ComponentProps<typeof Component>> & {
children: ReactNode
}
>
}
type OmitMotionProps<Props> = Omit<Props, keyof MotionProps>
You can then wrap components easily and with type safety:
const AnimatedBox = animated(Box)
// the above will have both the MotionProps and the BoxProps
// the former takes precedence over the latter
/cc @henriquea
@petecorreia Thanks for taking a proper look at this and for the clear explanation. I'm going to take a stab at this along the lines you describe.
// // Utility to create a type-safe motion.custom // // Example: // // const AnimatedBox = animated(Box) // export function animated<Props = {}>(Component: ComponentType<Props>) { return motion.custom(Component) as ForwardRefExoticComponent< MotionProps & RefAttributes<Element> & OmitMotionProps<ComponentProps<typeof Component>> & { children: ReactNode } > } type OmitMotionProps<Props> = Omit<Props, keyof MotionProps>
the utility function you shared isn't working for me, can you please share the full code
Most helpful comment
Ran into this issue too and I believe a good fix could be if the type ends up as this:
Highlights:
This would need no further changes to the
motion.customAPI and would _just work_.I'll try a PR later but have no time at the moment to debug why the bootstrap command is failing on my mac.
I think the line changes needed should be around here:
Haven't tested it, but it must be along those lines.
In the meantime here's a utility function
animatedthat implements it:You can then wrap components easily and with type safety:
/cc @henriquea