During this 45-minute video call, you’ll complete three front-end exercises (~15 minutes each). Our stack includes Next.js (App Router), TypeScript, SCSS, and follows an Atomic Design pattern with atoms
, molecules
, and organisms
folder structures.
You're encouraged to:
- Think aloud and walk us through your decisions.
- Keep consistency with code style and component organization.
- Discuss trade-offs, accessibility, and component design patterns.
Build a reusable <Button />
component that supports the following:
- Variants:
primary
,secondary
,text
- States:
default
,disabled
,loading
,success
- Styling: SCSS module with minimal visual feedback
The button will be rendered inside a pre-built form (provided). Your job is only to implement the Button
component, keeping the style, state, and accessibility consistent with modern patterns.
You’ll receive a components/molecules/form/component.tsx
file that looks like this:
'use client';
import { useState, type FunctionComponent } from 'react';
import { Button } from '@/components/atoms/button/component';
export const Form: FunctionComponent = () => {
const [state, setState] = useState<'default' | 'loading' | 'success'>('default');
const handleSubmit = async () => {
setState('loading');
await new Promise((r) => setTimeout(r, 1500));
setState('success');
setTimeout(() => setState('default'), 2000);
};
return (
<form onSubmit={(e) => { e.preventDefault(); handleSubmit(); }}>
<Button
variant="primary"
state={state}
onClick={handleSubmit}
disabled={state === 'loading'}
>
Submit
</Button>
</form>
);
}
- Implement components/atoms/button/component.tsx and component.module.scss
- Follow atomic design principles and clean component structure
- Use TypeScript to type all props
Using the Next.js App Router, build the following component structure that demonstrates proper server/client boundaries:
UserInfo (Server Component)
└── UserFormWrapper (Client Component)
└── UserName (Server Component)
-
UserInfo – Server Component
- Fetches user data using a simulated server-side call (e.g. getUser()).
- Renders a styled container (e.g. a card or div) with background color.
- Calls and renders the
<UserFormWrapper/>
component.
-
UserFormWrapper – Client Component
- Renders an input field and the submit button from Exercise 1.
- Simulates a form, use the example code from Exercise 1 (, and no need to update the actual server name).
- Wraps the
<UserName />
server component.
-
UserName – Server Component
- Receives the user name as a prop.
- Renders a simple
<h2>
element with “Welcome back, Alice” or similar.
- Proper use of use client directive.
- Understand the “client wrapping server” pattern and how Next.js composes them.
- Use static/mock data (e.g. getUser() returns { name: 'Alice' }).
- Organize files:
- components/organisms/user-info/component.tsx
- components/molecules/user-form-wrapper/component.tsx
- components/atoms/user-name/component.tsx
- Correctly configured boundaries between server and client components
- Proper props handling between components
- Ability to explain when server vs client should be used
- Understanding of what breaks composition and what doesn’t
The following component renders fine, but logs stale data under certain conditions. Your task is to find the bug, explain it, and fix it.
// components/organisms/Tracker.tsx
'use client';
import { useEffect, useState } from 'react';
type TrackerProps = {
id: string;
};
export function Tracker({ id }: TrackerProps) {
const [internalId, setInternalId] = useState(id);
useEffect(() => {
console.log('[Tracker] Currently tracking:', internalId);
});
return <div>Tracking ID: {internalId}</div>;
}
- Identify and fix the bug
- Explain why this bug can go unnoticed
- Discuss when useEffect runs, and how React handles closures and stale values
Throughout the exercises, we’re looking for:
- Clear structure and reusability in component design
- Awareness of accessibility and visual states
- Understanding of React’s reactivity model and lifecycle
- Solid grasp of server/client boundaries in the Next.js App Router