Skip to content
Merged
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"/>
```

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@SokratisVidros , @nikosdouvlis i have also added examples in changeset

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) => {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems that you're not using the HOC for the components that are mounted in paths we can infer using the env variables.

I feel like we should align this behavior by either:

  • making the Hoc generic enough to support both cases
  • supported env variables for all components

Do you want to discuss offline for possible solutions? :)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

☝️

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep I thought about that too, but the path of <SignIn/> and <SignUp/> can be also defined from the Clerk options on the <ClerkProvider/> and I didn't know if we wanted to make the changes to have ENV and options for every component.

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' />

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❓ Will this error on dev time due to missing type?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This error will be shown when the component is being rendered. This is a runtime error

</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