T
TanStack2y ago
flat-fuchsia

is there a way to create a reusable <Link /> component?

I'd like to create a reusable link component with styles but have access to the to prop so i have autocompletion for routes
14 Replies
flat-fuchsia
flat-fuchsiaOP2y ago
const LinkComponent = React.forwardRef<HTMLAnchorElement, LinkProps>(
(props, ref) => {
return (
<Link {...props} style={{ all: 'unset', cursor: 'pointer' }} ref={ref} />
);
}
);
const LinkComponent = React.forwardRef<HTMLAnchorElement, LinkProps>(
(props, ref) => {
return (
<Link {...props} style={{ all: 'unset', cursor: 'pointer' }} ref={ref} />
);
}
);
got it!
relaxed-coral
relaxed-coral2y ago
I do it like this, not only to props but params etc has typesafety too.
export const MenuItem = <
TRouteTree extends AnyRoute = RegisteredRouter["routeTree"],
TFrom extends RoutePaths<TRouteTree> | string = string,
TTo extends string = "",
TMaskFrom extends RoutePaths<TRouteTree> | string = TFrom,
TMaskTo extends string = "",
>(
props: LinkProps<TRouteTree, TFrom, TTo, TMaskFrom, TMaskTo> &
RefAttributes<HTMLAnchorElement> & {
label: string;
}
) => (
<Link
{...props}
className="pb-0.5"
activeProps={{ className: "border-b-[3px] border-primary " }}
activeOptions={{ exact: false, includeHash: true, includeSearch: true }}
>
{props.label}
</Link>
);
export const MenuItem = <
TRouteTree extends AnyRoute = RegisteredRouter["routeTree"],
TFrom extends RoutePaths<TRouteTree> | string = string,
TTo extends string = "",
TMaskFrom extends RoutePaths<TRouteTree> | string = TFrom,
TMaskTo extends string = "",
>(
props: LinkProps<TRouteTree, TFrom, TTo, TMaskFrom, TMaskTo> &
RefAttributes<HTMLAnchorElement> & {
label: string;
}
) => (
<Link
{...props}
className="pb-0.5"
activeProps={{ className: "border-b-[3px] border-primary " }}
activeOptions={{ exact: false, includeHash: true, includeSearch: true }}
>
{props.label}
</Link>
);
flat-fuchsia
flat-fuchsiaOP2y ago
thank you so much @JfrAziz
exotic-emerald
exotic-emerald2y ago
there now is also the experimental function createLink see https://github.com/TanStack/router/commit/c2fbb56b251d10f2c2832916ea60a779c079ae24 not yet documented but you could try it
foreign-sapphire
foreign-sapphire2y ago
Is there way to achieve previous versions Link functionality @Manuel Schiller I am not sure whether createlink is good way to create custom link as most of UI libraries supports as or custom component prop passing Link component directly looks better to me but it's broken on latest version for now I have to revert to old versions
exotic-emerald
exotic-emerald2y ago
@Bhunter can you please comment here? https://discord.com/channels/719702312431386674/1214176201216630784 let us know why this does not work for your use case
relaxed-coral
relaxed-coral2y ago
@Manuel Schiller i get this error use createLink or am I wrong?
Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()?
Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()?
export const MenuItem = createLink(
(props: LinkProps & ButtonProps & { label: string }) => (
<Button
size="sm"
variant="ghost"
className="pb-0.5"
activeProps={{ className: "border-b-[3px] border-primary " }}
activeOptions={{ exact: false, includeHash: true, includeSearch: true }}
{...props}
>
<a>{props.label}</a>
</Button>
)
);
export const MenuItem = createLink(
(props: LinkProps & ButtonProps & { label: string }) => (
<Button
size="sm"
variant="ghost"
className="pb-0.5"
activeProps={{ className: "border-b-[3px] border-primary " }}
activeOptions={{ exact: false, includeHash: true, includeSearch: true }}
{...props}
>
<a>{props.label}</a>
</Button>
)
);
exotic-emerald
exotic-emerald2y ago
@Tanner Linsley is this something createLinkshould support?
continuing-cyan
continuing-cyan2y ago
We are using forwardRef under the hood, so I'll need more details...
exotic-emerald
exotic-emerald2y ago
I think the example above is all that is needed here ?
continuing-cyan
continuing-cyan2y ago
ahhhhhhhh Yeah, let me fix this Actually I think the consumer needs to also forward ref Try this:
export const MenuItem = createLink(React.forwardRef(
(props: LinkProps & ButtonProps & { label: string }, ref) => (
<Button
size="sm"
variant="ghost"
className="pb-0.5"
activeProps={{ className: "border-b-[3px] border-primary " }}
activeOptions={{ exact: false, includeHash: true, includeSearch: true }}
{...props}
ref={ref}
>
<a>{props.label}</a>
</Button>
)
));
export const MenuItem = createLink(React.forwardRef(
(props: LinkProps & ButtonProps & { label: string }, ref) => (
<Button
size="sm"
variant="ghost"
className="pb-0.5"
activeProps={{ className: "border-b-[3px] border-primary " }}
activeOptions={{ exact: false, includeHash: true, includeSearch: true }}
{...props}
ref={ref}
>
<a>{props.label}</a>
</Button>
)
));
And let me know if typescript dies
flat-fuchsia
flat-fuchsiaOP2y ago
oo would love a page on the canonical way to create a reusable link in tanstack router! im also down to contribute!
flat-fuchsia
flat-fuchsia2y ago
@char and others, any progress on docs for this feature? Besides just reusable Link component I am looking into creating reusable component that is optionally a link. For this reason I have something like:
type MyCompProps = {
label: string,
link?: LinkProps
}

function MyComp(props: MyCompProps) {
if (props.link) return <Link {...props.link} />
}
type MyCompProps = {
label: string,
link?: LinkProps
}

function MyComp(props: MyCompProps) {
if (props.link) return <Link {...props.link} />
}
But in the <Link /> component I have the error of:
Type '{ children: Element; to?: ToPathOption<Route<any, "/", "/", string, "__root__", RootSearchSchema, RootSearchSchema, RootSearchSchema, ... 12 more ..., any>, string, ""> | undefined; ... 15 more ...; inactiveProps?: AnchorHTMLAttributes<...> | ... 1 more ... | undefined; }' is not assignable to type 'Omit<{ to?: ToPathOption<Route<any, "/", "/", string, "__root__", RootSearchSchema, RootSearchSchema, RootSearchSchema, RootSearchSchema, ... 11 more ..., any>, string, "" | ... 30 more ... | "/terms"> | undefined; hash?: true | ... 1 more ... | undefined; state?: true | ... 1 more ... | undefined; from?: string | u...'.
Types of property 'params' are incompatible.
Type 'true | ParamsReducer<{} | {} | { instanceResourceId: string; } | { configurationResourceId: string; } | { apiKeyResourceId: string; } | { policyResourceId: string; }, {} | {}> | undefined' is not assignable to type '(current: {} | {} | { instanceResourceId: string; } | { configurationResourceId: string; } | { apiKeyResourceId: string; } | { policyResourceId: string; }) => never'.
Type 'undefined' is not assignable to type '(current: {} | {} | { instanceResourceId: string; } | { configurationResourceId: string; } | { apiKeyResourceId: string; } | { policyResourceId: string; }) => never'.ts(2322)
Type '{ children: Element; to?: ToPathOption<Route<any, "/", "/", string, "__root__", RootSearchSchema, RootSearchSchema, RootSearchSchema, ... 12 more ..., any>, string, ""> | undefined; ... 15 more ...; inactiveProps?: AnchorHTMLAttributes<...> | ... 1 more ... | undefined; }' is not assignable to type 'Omit<{ to?: ToPathOption<Route<any, "/", "/", string, "__root__", RootSearchSchema, RootSearchSchema, RootSearchSchema, RootSearchSchema, ... 11 more ..., any>, string, "" | ... 30 more ... | "/terms"> | undefined; hash?: true | ... 1 more ... | undefined; state?: true | ... 1 more ... | undefined; from?: string | u...'.
Types of property 'params' are incompatible.
Type 'true | ParamsReducer<{} | {} | { instanceResourceId: string; } | { configurationResourceId: string; } | { apiKeyResourceId: string; } | { policyResourceId: string; }, {} | {}> | undefined' is not assignable to type '(current: {} | {} | { instanceResourceId: string; } | { configurationResourceId: string; } | { apiKeyResourceId: string; } | { policyResourceId: string; }) => never'.
Type 'undefined' is not assignable to type '(current: {} | {} | { instanceResourceId: string; } | { configurationResourceId: string; } | { apiKeyResourceId: string; } | { policyResourceId: string; }) => never'.ts(2322)
exotic-emerald
exotic-emerald2y ago
did you try createLink?

Did you find this page helpful?