Date: 2025-22-09

Status: accepted

Project Structure

src/
  pages/
    login/
      ui/page.tsx
  features/
    auth/
      ui/login-form.tsx
      model/use-login.ts
  entities/
    user/
      model/types.ts
  shared/
    ui/                # Facade for Core UI
      index.ts
  core-ui/             # Internal UI-kit (Atomic Design)
    atoms/
      Button.tsx
      Input.tsx
    molecules/
      FormField.tsx
    organisms/
      Modal.tsx
    index.ts

Core UI (Atomic Design)

core-ui/atoms/Button.tsx

import React from "react";

type ButtonProps = React.ButtonHTMLAttributes<HTMLButtonElement> & {
  variant?: "primary" | "secondary";
};

export const Button: React.FC<ButtonProps> = ({ variant = "primary", ...props }) => {
  const base = "px-4 py-2 rounded font-medium";
  const styles =
    variant === "primary"
      ? "bg-blue-600 text-white hover:bg-blue-700"
      : "bg-gray-200 text-gray-800 hover:bg-gray-300";

  return <button className={`${base} ${styles}`} {...props} />;
};

core-ui/atoms/Input.tsx

import React from "react";

type InputProps = React.InputHTMLAttributes<HTMLInputElement>;

export const Input: React.FC<InputProps> = (props) => {
  return (
    <input
      {...props}
      className="border border-gray-300 rounded px-3 py-2 w-full focus:outline-none focus:ring focus:ring-blue-400"
    />
  );
};

core-ui/molecules/FormField.tsx

import React from "react";

type FormFieldProps = {
  label: string;
  children: React.ReactNode;
};

export const FormField: React.FC<FormFieldProps> = ({ label, children }) => (
  <label className="block mb-4">
    <span className="text-gray-700 mb-1 block">{label}</span>
    {children}
  </label>
);

core-ui/index.ts

export { Button } from "./atoms/Button";
export { Input } from "./atoms/Input";
export { FormField } from "./molecules/FormField";

Shared Layer (FSD)

shared/ui/index.ts

// Facade for Core UI — upper layers should not import directly from core-ui
export { Button, Input, FormField } from "core-ui";

Feature: Auth

features/auth/model/use-login.ts

import { useState } from "react";

export function useLogin() {
  const [loading, setLoading] = useState(false);

  const login = async (email: string, password: string) => {
    setLoading(true);
    // simulate API request
    await new Promise((res) => setTimeout(res, 1000));
    setLoading(false);
    return { id: 1, name: "Demo User", email };
  };

  return { login, loading };
}

features/auth/ui/login-form.tsx

import React, { useState } from "react";
import { Button, Input, FormField } from "@/shared/ui";
import { useLogin } from "../model/use-login";

export const LoginForm: React.FC = () => {
  const { login, loading } = useLogin();
  const [email, setEmail] = useState("");
  const [password, setPassword] = useState("");

  const onSubmit = async (e: React.FormEvent) => {
    e.preventDefault();
    const user = await login(email, password);
    console.log("Logged in:", user);
  };

  return (
    <form onSubmit={onSubmit} className="max-w-sm mx-auto mt-8">
      <FormField label="Email">
        <Input type="email" value={email} onChange={(e) => setEmail(e.target.value)} />
      </FormField>
      <FormField label="Password">
        <Input type="password" value={password} onChange={(e) => setPassword(e.target.value)} />
      </FormField>
      <Button type="submit" disabled={loading}>
        {loading ? "Loading..." : "Login"}
      </Button>
    </form>
  );
};

Page (SSR / Routing)

pages/login/ui/page.tsx

import React from "react";
import { LoginForm } from "@/features/auth/ui/login-form";

export default function LoginPage() {
  return (
    <div className="flex items-center justify-center min-h-screen bg-gray-50">
      <div className="bg-white shadow p-6 rounded w-full max-w-md">
        <h1 className="text-xl font-bold mb-4">Sign in</h1>
        <LoginForm />
      </div>
    </div>
  );
}

Summary

  • Core UI (Atomic): independent reusable components (Button, Input, FormField).
  • Shared UI (FSD): facade for Core UI to enforce dependency rules (Included Business Logic).
  • Feature (Auth): LoginForm feature using Core UI components.
  • Page: /login page assembling the feature and layout.