Skip to content

Commit

Permalink
feat(clerk-react,remix,nextjs): Make path the default routing strateg…
Browse files Browse the repository at this point in the history
…y for NextJS and Remix (#2338)

* feat(clerk-react,remix,nextjs): Default routing for NextJS and Remix

* chore(repo): Update changeset

* chore(repo): Update NextJs playground

* fix(nextjs,remix): Throw error if no path or routing strategy provided

* fix(nextjs,remix,clerk-react): Improve error for withPathDefaultRouting

* chore(remix,nextjs,clerk-react): Replace withPathDefaultRouting with useRoutingOptions

* chore(remix,nextjs): Use hard-coded component name
This change is applied to show correct component name.
Using Component.displayName was causing a `<withClerk(Component)/>` to be shown in error message

* chore(repo): Update changeset with examples

* chore(repo): Disable telemetry in integration tests

---------

Co-authored-by: Dimitris Klouvas <dimitris@clerk.dev>
  • Loading branch information
octoper and dimkl committed Dec 19, 2023
1 parent 52b75d0 commit cfea3d9
Show file tree
Hide file tree
Showing 10 changed files with 128 additions and 60 deletions.
31 changes: 31 additions & 0 deletions .changeset/good-buttons-drum.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
---
'@clerk/clerk-js': major
'@clerk/nextjs': major
'@clerk/clerk-react': major
'@clerk/remix': major
---

Path-based routing is now the default routing strategy if the `path` prop is filled. Additionally, if the `path` and `routing` props are not filled, an error will be thrown.
```jsx

// Without path or routing props, an error with be thrown
<UserProfile />
<CreateOrganization />
<OrganizationProfile />
<SignIn />
<SignUp />

// Alternative #1
<UserProfile path="/whatever"/>
<CreateOrganization path="/whatever"/>
<OrganizationProfile path="/whatever"/>
<SignIn path="/whatever"/>
<SignUp path="/whatever"/>

// Alternative #2
<UserProfile routing="hash_or_virtual"/>
<CreateOrganization routing="hash_or_virtual"/>
<OrganizationProfile routing="hash_or_virtual"/>
<SignIn routing="hash_or_virtual"/>
<SignUp routing="hash_or_virtual"/>
```
3 changes: 3 additions & 0 deletions integration/presets/envs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ const envKeys = getInstanceKeys();

const withEmailCodes = environmentConfig()
.setId('withEmailCodes')
.setEnvVariable('public', 'CLERK_TELEMETRY_DISABLED', true)
.setEnvVariable('private', 'CLERK_SECRET_KEY', envKeys['with-email-codes'].sk)
.setEnvVariable('public', 'CLERK_PUBLISHABLE_KEY', envKeys['with-email-codes'].pk)
.setEnvVariable('public', 'CLERK_SIGN_IN_URL', '/sign-in')
Expand All @@ -32,6 +33,7 @@ const withEmailCodes = environmentConfig()

const withEmailLinks = environmentConfig()
.setId('withEmailLinks')
.setEnvVariable('public', 'CLERK_TELEMETRY_DISABLED', true)
.setEnvVariable('private', 'CLERK_SECRET_KEY', envKeys['with-email-links'].sk)
.setEnvVariable('public', 'CLERK_PUBLISHABLE_KEY', envKeys['with-email-links'].pk)
.setEnvVariable('public', 'CLERK_SIGN_IN_URL', '/sign-in')
Expand All @@ -40,6 +42,7 @@ const withEmailLinks = environmentConfig()

const withCustomRoles = environmentConfig()
.setId('withCustomRoles')
.setEnvVariable('public', 'CLERK_TELEMETRY_DISABLED', true)
// Temporarily use the stage api until the custom roles feature is released to prod
.setEnvVariable('private', 'CLERK_API_URL', 'https://api.clerkstage.dev')
.setEnvVariable('private', 'CLERK_SECRET_KEY', envKeys['with-custom-roles'].sk)
Expand Down
62 changes: 31 additions & 31 deletions packages/nextjs/src/client-boundary/uiComponents.tsx
Original file line number Diff line number Diff line change
@@ -1,52 +1,52 @@
'use client';

import { SignIn as BaseSignIn, SignUp as BaseSignUp } from '@clerk/clerk-react';
import type { SignInProps, SignUpProps } from '@clerk/types';
import {
CreateOrganization as BaseCreateOrganization,
OrganizationProfile as BaseOrganizationProfile,
SignIn as BaseSignIn,
SignUp as BaseSignUp,
UserProfile as BaseUserProfile,
} from '@clerk/clerk-react';
import { useRoutingProps } from '@clerk/clerk-react/internal';
import type {
CreateOrganizationProps,
OrganizationProfileProps,
SignInProps,
SignUpProps,
UserProfileProps,
} from '@clerk/types';
import React from 'react';

import { useClerkNextOptions } from './NextOptionsContext';

export {
CreateOrganization,
OrganizationList,
OrganizationProfile,
OrganizationSwitcher,
SignInButton,
SignInWithMetamaskButton,
SignOutButton,
SignUpButton,
UserButton,
UserProfile,
} from '@clerk/clerk-react';

export const UserProfile = (props: UserProfileProps) => {
return <BaseUserProfile {...useRoutingProps('UserProfile', props)} />;
};

export const CreateOrganization = (props: CreateOrganizationProps) => {
return <BaseCreateOrganization {...useRoutingProps('CreateOrganization', props)} />;
};

export const OrganizationProfile = (props: OrganizationProfileProps) => {
return <BaseOrganizationProfile {...useRoutingProps('OrganizationProfile', props)} />;
};

export const SignIn = (props: SignInProps) => {
const { signInUrl: repoLevelSignInUrl } = useClerkNextOptions();
const path = props.path || repoLevelSignInUrl;

if (path) {
<BaseSignIn
{...props}
routing='path'
path={path}
/>;
}

return <BaseSignIn {...props} />;
const { signInUrl } = useClerkNextOptions();
return <BaseSignIn {...useRoutingProps('SignIn', props, { path: signInUrl })} />;
};

export const SignUp = (props: SignUpProps) => {
const { signUpUrl: repoLevelSignUpUrl } = useClerkNextOptions();
const path = props.path || repoLevelSignUpUrl;

if (path) {
return (
<BaseSignUp
{...props}
routing='path'
path={path}
/>
);
}

return <BaseSignUp {...props} />;
const { signUpUrl } = useClerkNextOptions();
return <BaseSignUp {...useRoutingProps('SignUp', props, { path: signUpUrl })} />;
};
3 changes: 3 additions & 0 deletions packages/react/src/errors/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,6 @@ export const customLinkWrongProps = (componentName: string) =>

export const useAuthHasRequiresRoleOrPermission =
'Missing parameters. `has` from `useAuth` requires a permission or role key to be passed. Example usage: `has({permission: "org:posts:edit"`';

export const noPathProvidedError = (componentName: string) =>
`<${componentName}/> is missing a path prop to work with path based routing`;
27 changes: 27 additions & 0 deletions packages/react/src/hooks/useRoutingProps.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import type { RoutingOptions } from '@clerk/types';

import { errorThrower } from '../errors/errorThrower';
import { noPathProvidedError } from '../errors/messages';

export function useRoutingProps<T extends RoutingOptions>(
componentName: string,
props: T,
routingOptions?: RoutingOptions,
): T {
if (!props.path && !props.routing) {
errorThrower.throw(noPathProvidedError(componentName));
}

if (props.path) {
return {
...routingOptions,
...props,
routing: 'path',
};
}

return {
...routingOptions,
...props,
};
}
1 change: 1 addition & 0 deletions packages/react/src/internal.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export { setErrorThrowerOptions } from './errors/errorThrower';
export { MultisessionAppSupport } from './components/controlComponents';
export { useRoutingProps } from './hooks/useRoutingProps';
55 changes: 29 additions & 26 deletions packages/remix/src/client/uiComponents.tsx
Original file line number Diff line number Diff line change
@@ -1,37 +1,40 @@
import { SignIn as BaseSignIn, SignUp as BaseSignUp } from '@clerk/clerk-react';
import type { SignInProps, SignUpProps } from '@clerk/types';
import {
CreateOrganization as BaseCreateOrganization,
OrganizationProfile as BaseOrganizationProfile,
SignIn as BaseSignIn,
SignUp as BaseSignUp,
UserProfile as BaseUserProfile,
} from '@clerk/clerk-react';
import { useRoutingProps } from '@clerk/clerk-react/internal';
import type {
CreateOrganizationProps,
OrganizationProfileProps,
SignInProps,
SignUpProps,
UserProfileProps,
} from '@clerk/types';
import React from 'react';

import { useClerkRemixOptions } from './RemixOptionsContext';

export const SignIn = (props: SignInProps) => {
const { signInUrl } = useClerkRemixOptions();
const path = props.path || signInUrl;
export const UserProfile = (props: UserProfileProps) => {
return <BaseUserProfile {...useRoutingProps('UserProfile', props)} />;
};

if (path) {
return (
<BaseSignIn
{...props}
routing='path'
path={path}
/>
);
}
export const CreateOrganization = (props: CreateOrganizationProps) => {
return <BaseCreateOrganization {...useRoutingProps('CreateOrganization', props)} />;
};

return <BaseSignIn {...props} />;
export const OrganizationProfile = (props: OrganizationProfileProps) => {
return <BaseOrganizationProfile {...useRoutingProps('OrganizationProfile', props)} />;
};

export const SignIn = (props: SignInProps) => {
const { signInUrl } = useClerkRemixOptions();
return <BaseSignIn {...useRoutingProps('SignIn', props, { path: signInUrl })} />;
};

export const SignUp = (props: SignUpProps) => {
const { signUpUrl } = useClerkRemixOptions();
const path = props.path || signUpUrl;

if (path) {
return (
<BaseSignUp
routing='path'
path={path}
/>
);
}
return <BaseSignUp {...props} />;
return <BaseSignUp {...useRoutingProps('SignUp', props, { path: signUpUrl })} />;
};
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ const CreateOrganizationPage: NextPage = () => {
<div
style={{ display: 'flex', flexDirection: 'column', gap: '2rem', justifyContent: 'center', alignItems: 'center' }}
>
<CreateOrganization />
<CreateOrganization path='/create-organization' />
</div>
);
};
Expand Down
2 changes: 1 addition & 1 deletion playground/nextjs/pages/organization/[[...index]].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ const OrganizationProfilePage: NextPage = () => {
<div
style={{ display: 'flex', flexDirection: 'column', gap: '2rem', justifyContent: 'center', alignItems: 'center' }}
>
<OrganizationProfile />
<OrganizationProfile path='/organization' />
</div>
);
};
Expand Down
2 changes: 1 addition & 1 deletion playground/nextjs/pages/user/[[...index]].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export const getServerSideProps: GetServerSideProps = async context => {
const UserProfilePage: NextPage = () => {
return (
<div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center' }}>
<UserProfile />
<UserProfile path='/user' />
</div>
);
};
Expand Down

0 comments on commit cfea3d9

Please sign in to comment.