Frontend Framework

React & Next.js

React ekosistemiyle SPA, SSR ve SSG destekli modern frontend uygulamaları geliştiriyoruz. Next.js App Router ile sunucu bileşenleri, TanStack Query ile akıllı veri yönetimi ve Zustand ile lightweight state management tercih ediyoruz.

React v19
Next.js v15
TypeScript v5
Runtime Node 22+

Bileşen Mimarisi ile
Ölçeklenebilir Arayüzler

React'ın bileşen bazlı mimarisi, büyük ölçekli uygulamaları izole, test edilebilir ve yeniden kullanılabilir parçalara ayırarak yönetmeyi kolaylaştırır. Next.js 15'in App Router'ı ile Server Components, Streaming ve Parallel Routes gibi modern rendering stratejilerini aynı proje içinde harmanlıyoruz.

TanStack Query v5 ile sunucu durumunu önbellekleme ve senkronizasyon; Zustand ile minimal ama güçlü client-side state yönetimi; React Hook Form + Zod ile tip güvenli form validasyonu uyguluyoruz. Tüm kod tabanı TypeScript ile yazılır, CI'da strict mod kontrolleri zorunludur.

Core Web Vitals optimizasyonu, Image & Font optimizasyonu, Edge Runtime desteği ve next-intl ile çok dilli yapı; üretim kalitesindeki her projede standart olarak yer alır.


Geliştirme Standartlarımız

  • App Router & Server Components Veri çekimini sunucuda gerçekleştirerek client bundle'ı minimumda tutuyoruz.
  • TypeScript Strict Mode any yasak; tüm API yanıtları ve store yapıları Zod şemalarıyla doğrulanır.
  • TanStack Query Cache Stratejisi staleTime, gcTime ve invalidation kuralları servis katmanında merkezi olarak tanımlanır.
  • Atomic Design + Feature Slicing atoms → molecules → organisms → features hiyerarşisi; feature-sliced architecture opsiyonel.
  • Optimistic UI & Error Boundaries Kullanıcı deneyimini bozmadan mutation hataları geri alınır; boundary'ler fallback UI gösterir.
  • Playwright E2E + Vitest Unit Kritik akışlar Playwright ile; hook ve store'lar Vitest + Testing Library ile test edilir.

Kod Örnekleri

hooks/useOrders.ts
TypeScript
import {
  useQuery,
  useMutation,
  useQueryClient,
  keepPreviousData,
} from '@tanstack/react-query';
import { ordersApi } from '@/lib/api/orders';
import type { Order, OrderFilters, CreateOrderDto } from '@/types/order';

// ─── Query Keys ──────────────────────────────────────────
export const orderKeys = {
  all:     ['orders']                            as const,
  lists:   () => [...orderKeys.all, 'list']     as const,
  list:    (f: OrderFilters) => [...orderKeys.lists(), f] as const,
  details: () => [...orderKeys.all, 'detail']   as const,
  detail:  (id: string) => [...orderKeys.details(), id]  as const,
};

// ─── useOrders Hook ──────────────────────────────────────
export function useOrders(filters: OrderFilters) {
  return useQuery({
    queryKey:    orderKeys.list(filters),
    queryFn:     () => ordersApi.getAll(filters),
    placeholderData: keepPreviousData,
    staleTime:   30_000,   // 30 sn taze kabul et
    select: (data) => ({
      orders:  data.items,
      total:   data.total,
      hasMore: data.page < data.totalPages,
    }),
  });
}

// ─── useCreateOrder Hook ─────────────────────────────────
export function useCreateOrder() {
  const queryClient = useQueryClient();

  return useMutation({
    mutationFn: (dto: CreateOrderDto) => ordersApi.create(dto),

    // Optimistic update: sipariş anında listeye eklenir
    onMutate: async (newOrder) => {
      await queryClient.cancelQueries({ queryKey: orderKeys.lists() });

      const snapshot = queryClient.getQueryData<{ items: Order[] }>(
        orderKeys.list({})
      );

      queryClient.setQueryData(orderKeys.list({}), (old: any) => ({
        ...old,
        items: [
          { ...newOrder, id: 'temp-' + Date.now(), status: 'pending' },
          ...(old?.items ?? []),
        ],
      }));

      return { snapshot };
    },

    // Hata durumunda snapshot'a geri dön
    onError: (_err, _vars, ctx) => {
      if (ctx?.snapshot) {
        queryClient.setQueryData(orderKeys.list({}), ctx.snapshot);
      }
    },

    // Başarı veya hata sonrası listeyi yenile
    onSettled: () => {
      queryClient.invalidateQueries({ queryKey: orderKeys.lists() });
    },
  });
}

// ─── useDeleteOrder Hook ─────────────────────────────────
export function useDeleteOrder() {
  const queryClient = useQueryClient();

  return useMutation({
    mutationFn: (id: string) => ordersApi.delete(id),
    onSuccess: (_data, deletedId) => {
      // Detay cache'ini temizle
      queryClient.removeQueries({ queryKey: orderKeys.detail(deletedId) });
      // Liste cache'ini invalidate et
      queryClient.invalidateQueries({ queryKey: orderKeys.lists() });
    },
  });
}
app/(dashboard)/orders/page.tsx
TSX
import { Suspense } from 'react';
import { notFound }  from 'next/navigation';
import type { Metadata, NextPage } from 'next';

import { getServerSession } from '@/lib/auth/session';
import { ordersService }    from '@/server/services/orders';
import { statsService }     from '@/server/services/stats';

import { OrdersTable }       from './_components/OrdersTable';
import { OrdersTableSkeleton } from './_components/OrdersTableSkeleton';
import { OrdersStats }       from './_components/OrdersStats';
import { OrdersFilters }     from './_components/OrdersFilters';

// ─── Metadata ────────────────────────────────────────────
export const metadata: Metadata = {
  title: 'Siparişler | Dashboard',
  description: 'Tüm sipariş yönetimi.',
};

// ─── Props ───────────────────────────────────────────────
interface OrdersPageProps {
  searchParams: Promise<{
    page?:   string;
    status?: 'pending' | 'processing' | 'shipped' | 'delivered';
    q?:      string;
    from?:   string;
    to?:     string;
  }>;
}

// ─── Page (async Server Component) ───────────────────────
const OrdersPage: NextPage<OrdersPageProps> = async ({ searchParams }) => {
  const session = await getServerSession();
  if (!session) notFound();

  const params = await searchParams;
  const page   = Number(params.page ?? 1);
  const status = params.status;
  const query  = params.q ?? '';

  // Paralel veri çekimi — iki fetch aynı anda başlar
  const [ordersData, statsData] = await Promise.all([
    ordersService.getPaginated({
      page,
      perPage: 20,
      status,
      query,
      dateFrom: params.from,
      dateTo:   params.to,
      userId:   session.user.id,
    }),
    statsService.getOrderSummary({ userId: session.user.id }),
  ]);

  return (
    <div className="space-y-6">
      {/* Başlık */}
      <div className="flex items-center justify-between">
        <div>
          <h1 className="text-2xl font-bold tracking-tight">Siparişler</h1>
          <p className="text-muted-foreground mt-1">
            Toplam {ordersData.total} sipariş listeleniyor.
          </p>
        </div>
      </div>

      {/* İstatistik kartları — server'da hesaplandı */}
      <OrdersStats stats={statsData} />

      {/* Filtreler (Client Component) */}
      <OrdersFilters defaultValues={{ status, q: query }} />

      {/* Tablo — Suspense ile streaming */}
      <Suspense
        key={`${page}-${status}-${query}`}
        fallback={<OrdersTableSkeleton rows={20} />}
      >
        <OrdersTable
          initialData={ordersData}
          page={page}
          filters={{ status, query }}
        />
      </Suspense>
    </div>
  );
};

export default OrdersPage;
stores/useAuthStore.ts
TypeScript
import { create }         from 'zustand';
import { persist, devtools } from 'zustand/middleware';
import { immer }           from 'zustand/middleware/immer';
import { authApi }         from '@/lib/api/auth';

// ─── Types ───────────────────────────────────────────────
interface User {
  id:       string;
  email:    string;
  name:     string;
  role:     'admin' | 'manager' | 'staff';
  avatar?:  string;
  tenantId: string;
}

interface AuthState {
  user:        User | null;
  token:       string | null;
  isLoading:   boolean;
  error:       string | null;
}

interface AuthActions {
  login:  (email: string, password: string) => Promise<void>;
  logout: () => Promise<void>;
  updateProfile: (data: Partial<Pick<User, 'name' | 'avatar'>>) => void;
  clearError: () => void;
}

type AuthStore = AuthState & AuthActions;

// ─── Store ───────────────────────────────────────────────
export const useAuthStore = create<AuthStore>()(
  devtools(
    persist(
      immer((set) => ({
        // Initial state
        user:      null,
        token:     null,
        isLoading: false,
        error:     null,

        // ── Actions ─────────────────────────────────────
        login: async (email, password) => {
          set((s) => { s.isLoading = true; s.error = null; });

          try {
            const { user, token } = await authApi.login({ email, password });
            set((s) => {
              s.user      = user;
              s.token     = token;
              s.isLoading = false;
            });
          } catch (err: any) {
            set((s) => {
              s.error     = err?.response?.data?.message ?? 'Giriş başarısız.';
              s.isLoading = false;
            });
          }
        },

        logout: async () => {
          try { await authApi.logout(); } catch { /* sessizce geç */ }
          set((s) => { s.user = null; s.token = null; });
        },

        updateProfile: (data) => {
          set((s) => {
            if (s.user) Object.assign(s.user, data);
          });
        },

        clearError: () => set((s) => { s.error = null; }),
      })),
      {
        name:    'auth-storage',
        partialize: (s) => ({ user: s.user, token: s.token }), // sadece bu alanlar persist edilir
      }
    ),
    { name: 'AuthStore' }
  )
);

// ─── Selector Hooks (gereksiz re-render önleme) ───────────
export const useCurrentUser  = () => useAuthStore((s) => s.user);
export const useIsAdmin      = () => useAuthStore((s) => s.user?.role === 'admin');
export const useAuthToken    = () => useAuthStore((s) => s.token);
export const useAuthLoading  = () => useAuthStore((s) => s.isLoading);
export const useAuthError    = () => useAuthStore((s) => s.error);
export const useAuthActions  = () =>
  useAuthStore((s) => ({
    login:         s.login,
    logout:        s.logout,
    updateProfile: s.updateProfile,
    clearError:    s.clearError,
  }));
components/orders/OrderForm.tsx
TSX
'use client';

import { useFieldArray, useForm } from 'react-hook-form';
import { zodResolver }            from '@hookform/resolvers/zod';
import { z }                      from 'zod';
import { useCreateOrder }         from '@/hooks/useOrders';

// ─── Zod Schema ──────────────────────────────────────────
const orderItemSchema = z.object({
  productId: z.string().min(1, 'Ürün seçiniz'),
  quantity:  z.coerce.number().int().min(1, 'En az 1 adet giriniz').max(9999),
  unitPrice: z.coerce.number().positive('Geçerli bir fiyat giriniz'),
  note:      z.string().max(200).optional(),
});

const orderSchema = z.object({
  customerId:      z.string().uuid('Geçerli bir müşteri seçiniz'),
  deliveryAddress: z.object({
    street:  z.string().min(5, 'Cadde/Sokak zorunludur'),
    city:    z.string().min(2),
    country: z.string().length(2, 'ISO 3166-1 alpha-2 kodu giriniz'),
    zip:     z.string().regex(/^\d{5}$/, 'Posta kodu 5 haneli olmalıdır'),
  }),
  priority:  z.enum(['low', 'normal', 'high', 'urgent']),
  items:     z.array(orderItemSchema).min(1, 'En az bir ürün ekleyin'),
  discount:  z.coerce.number().min(0).max(100).optional(),
  notes:     z.string().max(500).optional(),
});

type OrderFormValues = z.infer<typeof orderSchema>;

// ─── Component ───────────────────────────────────────────
interface OrderFormProps {
  customers: { id: string; name: string }[];
  onSuccess?: () => void;
}

export function OrderForm({ customers, onSuccess }: OrderFormProps) {
  const createOrder = useCreateOrder();

  const form = useForm<OrderFormValues>({
    resolver: zodResolver(orderSchema),
    defaultValues: {
      priority: 'normal',
      items:    [{ productId: '', quantity: 1, unitPrice: 0 }],
      discount: 0,
    },
  });

  const { fields, append, remove } = useFieldArray({
    control: form.control,
    name:    'items',
  });

  const onSubmit = async (values: OrderFormValues) => {
    await createOrder.mutateAsync(values, {
      onSuccess: () => {
        form.reset();
        onSuccess?.();
      },
    });
  };

  return (
    <form onSubmit={form.handleSubmit(onSubmit)} className="space-y-6">

      {/* Müşteri */}
      <div>
        <label className="label">Müşteri</label>
        <select
          {...form.register('customerId')}
          className="input"
        >
          <option value="">Seçiniz…</option>
          {customers.map((c) => (
            <option key={c.id} value={c.id}>{c.name}</option>
          ))}
        </select>
        {form.formState.errors.customerId && (
          <p className="field-error">{form.formState.errors.customerId.message}</p>
        )}
      </div>

      {/* Öncelik */}
      <div>
        <label className="label">Öncelik</label>
        <select {...form.register('priority')} className="input">
          <option value="low">Düşük</option>
          <option value="normal">Normal</option>
          <option value="high">Yüksek</option>
          <option value="urgent">Acil</option>
        </select>
      </div>

      {/* Sipariş Kalemleri */}
      <div>
        <div className="flex items-center justify-between mb-3">
          <h3 className="font-semibold">Ürünler</h3>
          <button
            type="button"
            onClick={() => append({ productId: '', quantity: 1, unitPrice: 0 })}
            className="btn btn-ghost"
            style={{ fontSize: '.85rem' }}
          >
            + Kalem Ekle
          </button>
        </div>

        {fields.map((field, idx) => (
          <div key={field.id} className="grid grid-cols-4 gap-3 mb-3 items-start">
            <input
              {...form.register(`items.${idx}.productId`)}
              placeholder="Ürün ID"
              className="input col-span-2"
            />
            <input
              {...form.register(`items.${idx}.quantity`)}
              type="number" min={1}
              placeholder="Adet"
              className="input"
            />
            <div className="flex gap-2">
              <input
                {...form.register(`items.${idx}.unitPrice`)}
                type="number" step="0.01"
                placeholder="Fiyat"
                className="input flex-1"
              />
              {fields.length > 1 && (
                <button
                  type="button"
                  onClick={() => remove(idx)}
                  className="btn btn-ghost"
                  aria-label="Kalemi sil"
                >✕</button>
              )}
            </div>
            {form.formState.errors.items?.[idx]?.productId && (
              <p className="field-error col-span-4">
                {form.formState.errors.items[idx]?.productId?.message}
              </p>
            )}
          </div>
        ))}
        {form.formState.errors.items?.root && (
          <p className="field-error">{form.formState.errors.items.root.message}</p>
        )}
      </div>

      {/* Gönder */}
      <button
        type="submit"
        className="btn btn-primary btn-lg btn-full"
        disabled={createOrder.isPending}
      >
        {createOrder.isPending ? 'Kaydediliyor…' : 'Siparişi Oluştur'}
      </button>

    </form>
  );
}

Ekosistem & Kütüphaneler

Next.js 15 latest

Full-stack React framework. App Router, Server/Client Components, Streaming SSR, Edge Runtime ve built-in Image optimizasyonu.

TanStack Query v5 stable

Server state yönetiminin altın standardı. Akıllı önbellek, background refetch, infinite scroll ve optimistic updates.

Zustand stable

Minimal ama güçlü global state. DevTools, persist ve immer middleware'leri ile production-ready.

React Hook Form + Zod stable

Performanslı form yönetimi ve tip güvenli schema validasyonu. zodResolver ile sıfır boilerplate.

Framer Motion stable

Bildirimsel animasyon kütüphanesi. Layout animations, shared element transitions ve scroll-driven efektler.

Radix UI / shadcn stable

Erişilebilir, unstyled headless bileşenler. Tailwind CSS ile özelleştirilmiş shadcn/ui katmanı.

next-intl stable

App Router destekli i18n çözümü. Server Component'larda çeviri, locale routing ve pluralization.

NextAuth.js / Auth.js v5

Tam entegre kimlik doğrulama. OAuth (Google, GitHub), credential, magic link ve JWT/session stratejileri.


React & Next.js ile
Ne İnşa Ediyoruz?

📊

Dashboard & Admin Panel

Gerçek zamanlı metrik grafikleri, role-based erişim kontrolü ve ağır veri tablolarıyla yönetim panelleri.

🛒

E-ticaret Frontend

Ürün katalogu, sepet, ödeme akışı ve kişiselleştirilmiş öneri sistemleriyle tam donanımlı alışveriş deneyimi.

🚀

SaaS Kullanıcı Arayüzü

Çok kiracılı mimari destekli, tenant bazlı özelleştirme ve karmaşık iş akışlarına sahip SaaS ürün UI'ları.

🌐

Landing & Marketing Sayfaları

Hız odaklı statik sayfalar; A/B test entegrasyonu, CMS bağlantısı ve yüksek Lighthouse skoru.

📱

Progressive Web App (PWA)

Offline desteği, push notification, install prompt ve native-benzeri kullanıcı deneyimi sunan PWA'lar.

🌍

Multi-language Uygulamalar

next-intl ile tam i18n: locale routing, RTL desteği, çeviri yönetimi ve SEO dostu URL yapıları.


React & Next.js ile Hızlı ve Ölçeklenebilir Bir Frontend mi İstiyorsunuz?

Mevcut React projenizi Next.js App Router'a migrate edebilir ya da sıfırdan production-ready bir frontend mimarisi inşa edebiliriz.