diff --git a/docs/docs/developer/frontend/style-guide.mdx b/docs/docs/developer/frontend/style-guide.mdx
index 6e85aefb9..dfd47a65d 100644
--- a/docs/docs/developer/frontend/style-guide.mdx
+++ b/docs/docs/developer/frontend/style-guide.mdx
@@ -85,6 +85,32 @@ const EmailField = ({ value }: OwnProps) => (
);
```
+#### No Single Variable Prop Spreading in JSX Elements
+
+We discourage the use of single variable prop spreading in JSX elements, e.g., `{...props}`. This practice often leads to less readable and maintainable code as it's unclear what props are being passed down to the component.
+
+```tsx
+/* ❌ - Bad, spreads a single variable prop into the underlying component
+ */
+const MyComponent = (props: OwnProps) => {
+ return ;
+}
+```
+
+```tsx
+/* ✅ - Good, Explicitly lists all props
+ * - Enhances readability and maintainability
+ */
+const MyComponent = ({ prop1, prop2, prop3 }: OwnProps) => {
+ return ;
+};
+```
+
+Rationale:
+- It's clearer to see at a glance which props are being passed down, making the code easier to understand and maintain.
+- It helps to prevent tight coupling between components via their props.
+- It's easier to catch misspelled or unused props with linting tools when props are listed explicitly
+
## JavaScript
### Use nullish-coalescing operator `??`
diff --git a/front/.eslintrc.js b/front/.eslintrc.js
index dc688765d..c0c06a4d5 100644
--- a/front/.eslintrc.js
+++ b/front/.eslintrc.js
@@ -62,6 +62,7 @@ module.exports = {
'simple-import-sort/exports': 'error',
'twenty/effect-components': 'error',
'twenty/no-hardcoded-colors': 'error',
+ 'twenty/no-spread-props': 'error',
'twenty/matching-state-variable': 'error',
'twenty/sort-css-properties-alphabetically': 'error',
'twenty/styled-components-prefixed-with-styled': 'error',
diff --git a/front/src/modules/activities/table/components/CellCommentChip.tsx b/front/src/modules/activities/table/components/CellCommentChip.tsx
index 17d2aba11..4027975c4 100644
--- a/front/src/modules/activities/table/components/CellCommentChip.tsx
+++ b/front/src/modules/activities/table/components/CellCommentChip.tsx
@@ -10,6 +10,7 @@ export const CellCommentChip = (props: CommentChipProps) => {
return (
+ {/* eslint-disable-next-line twenty/no-spread-props */}
);
diff --git a/front/src/modules/auth/components/Logo.tsx b/front/src/modules/auth/components/Logo.tsx
index 0c561e90c..003559a9d 100644
--- a/front/src/modules/auth/components/Logo.tsx
+++ b/front/src/modules/auth/components/Logo.tsx
@@ -50,6 +50,7 @@ const StyledMainLogo = styled.div`
export const Logo = ({ workspaceLogo, ...props }: Props) => {
if (!workspaceLogo) {
return (
+ // eslint-disable-next-line twenty/no-spread-props
@@ -57,6 +58,7 @@ export const Logo = ({ workspaceLogo, ...props }: Props) => {
}
return (
+ // eslint-disable-next-line twenty/no-spread-props
diff --git a/front/src/modules/auth/components/Modal.tsx b/front/src/modules/auth/components/Modal.tsx
index 3c7aad04e..765b04aa7 100644
--- a/front/src/modules/auth/components/Modal.tsx
+++ b/front/src/modules/auth/components/Modal.tsx
@@ -11,6 +11,7 @@ const StyledContent = styled(UIModal.Content)`
type Props = React.ComponentProps<'div'>;
export const AuthModal = ({ children, ...restProps }: Props) => (
+ // eslint-disable-next-line twenty/no-spread-props
{children}
diff --git a/front/src/modules/auth/sign-in-up/components/FooterNote.tsx b/front/src/modules/auth/sign-in-up/components/FooterNote.tsx
index 8d6e6f052..cd41568fb 100644
--- a/front/src/modules/auth/sign-in-up/components/FooterNote.tsx
+++ b/front/src/modules/auth/sign-in-up/components/FooterNote.tsx
@@ -11,4 +11,5 @@ const StyledContainer = styled.div`
text-align: center;
`;
+// eslint-disable-next-line twenty/no-spread-props
export const FooterNote = (props: Props) => ;
diff --git a/front/src/modules/spreadsheet-import/components/Heading.tsx b/front/src/modules/spreadsheet-import/components/Heading.tsx
index 04c6873da..d35a7e620 100644
--- a/front/src/modules/spreadsheet-import/components/Heading.tsx
+++ b/front/src/modules/spreadsheet-import/components/Heading.tsx
@@ -28,6 +28,7 @@ const StyledDescription = styled.span`
`;
export const Heading = ({ title, description, ...props }: Props) => (
+ // eslint-disable-next-line twenty/no-spread-props
{title}
{description && {description}}
diff --git a/front/src/modules/spreadsheet-import/components/Table.tsx b/front/src/modules/spreadsheet-import/components/Table.tsx
index 9f840f507..8971191df 100644
--- a/front/src/modules/spreadsheet-import/components/Table.tsx
+++ b/front/src/modules/spreadsheet-import/components/Table.tsx
@@ -115,6 +115,7 @@ export const Table = (props: Props) => {
const { rtl } = useSpreadsheetImportInternal();
return (
+ // eslint-disable-next-line twenty/no-spread-props
);
};
diff --git a/front/src/modules/ui/button/components/MainButton.tsx b/front/src/modules/ui/button/components/MainButton.tsx
index 81740185b..34726ede6 100644
--- a/front/src/modules/ui/button/components/MainButton.tsx
+++ b/front/src/modules/ui/button/components/MainButton.tsx
@@ -107,6 +107,7 @@ export const MainButton = ({
}: MainButtonProps) => {
const theme = useTheme();
return (
+ // eslint-disable-next-line twenty/no-spread-props
{Icon && }
{title}
diff --git a/front/src/modules/ui/button/components/RoundedIconButton.tsx b/front/src/modules/ui/button/components/RoundedIconButton.tsx
index bf8092581..3c7ccccba 100644
--- a/front/src/modules/ui/button/components/RoundedIconButton.tsx
+++ b/front/src/modules/ui/button/components/RoundedIconButton.tsx
@@ -40,6 +40,7 @@ export const RoundedIconButton = ({
const theme = useTheme();
return (
+ // eslint-disable-next-line twenty/no-spread-props
{}
diff --git a/front/src/modules/ui/checkmark/components/AnimatedCheckmark.tsx b/front/src/modules/ui/checkmark/components/AnimatedCheckmark.tsx
index 6732fce5b..8261c9e10 100644
--- a/front/src/modules/ui/checkmark/components/AnimatedCheckmark.tsx
+++ b/front/src/modules/ui/checkmark/components/AnimatedCheckmark.tsx
@@ -24,6 +24,7 @@ export const AnimatedCheckmark = ({
height={size}
>
{
const theme = useTheme();
return (
+ // eslint-disable-next-line twenty/no-spread-props
diff --git a/front/src/modules/ui/color-scheme/components/ColorSchemeCard.tsx b/front/src/modules/ui/color-scheme/components/ColorSchemeCard.tsx
index f9f265b9a..9166a78d1 100644
--- a/front/src/modules/ui/color-scheme/components/ColorSchemeCard.tsx
+++ b/front/src/modules/ui/color-scheme/components/ColorSchemeCard.tsx
@@ -105,6 +105,7 @@ const ColorSchemeSegment = ({
controls,
...rest
}: ColorSchemeSegmentProps) => (
+ // eslint-disable-next-line twenty/no-spread-props
Aa
@@ -173,6 +174,7 @@ export const ColorSchemeCard = ({
diff --git a/front/src/modules/ui/dialog/components/Dialog.tsx b/front/src/modules/ui/dialog/components/Dialog.tsx
index 2c5dc0c7b..2986917fd 100644
--- a/front/src/modules/ui/dialog/components/Dialog.tsx
+++ b/front/src/modules/ui/dialog/components/Dialog.tsx
@@ -137,6 +137,7 @@ export const Dialog = ({
{title && {title}}
@@ -151,6 +152,7 @@ export const Dialog = ({
}}
fullWidth={true}
variant={button.variant ?? 'secondary'}
+ // eslint-disable-next-line twenty/no-spread-props
{...button}
/>
))}
diff --git a/front/src/modules/ui/dialog/components/DialogProvider.tsx b/front/src/modules/ui/dialog/components/DialogProvider.tsx
index 7f0c5e7cf..fa90897ce 100644
--- a/front/src/modules/ui/dialog/components/DialogProvider.tsx
+++ b/front/src/modules/ui/dialog/components/DialogProvider.tsx
@@ -40,6 +40,7 @@ export const DialogProvider = ({ children }: React.PropsWithChildren) => {
{dialogInternal.queue.map((dialog) => (