React DatePickerの使い方|日付選択UIを10分で実装

React DatePickerライブラリを使った日付選択UIの実装方法を詳しく解説。基本的な使い方から日本語化、カスタマイズまで初心者向けに説明します。

Learning Next 運営
36 分で読めます

みなさん、Reactで日付選択機能を実装したいと思ったことはありませんか?

「予約システムで日付を選択させたい」 「フォームに生年月日入力欄を追加したい」 「期間指定の検索機能を作りたい」

そんな疑問を持ったことはありませんか?

実は、React DatePickerを使えば、わずか10分で実用的な日付選択UIが作れるんです。 この記事では、react-datepickerライブラリを使って日付選択機能を実装する方法を詳しく解説します。

基本的な使い方から日本語化、カスタマイズまで、初心者の方でも分かりやすく説明していきます。 一緒に素敵な日付選択UIを作ってみましょう!

React DatePickerって何?

まず、React DatePickerの基本的な特徴を理解しましょう。

ライブラリの特徴

React DatePickerは、最も人気の高いReact用日付選択ライブラリです。 簡単に言うと、「カレンダーから日付を選ぶ機能」を簡単に追加できるライブラリです。

こんな特徴があります。

  • 軽量で高性能: アプリの動作が重くならない
  • 豊富なカスタマイズ: 見た目や機能を自由に変更可能
  • アクセシビリティ対応: 誰でも使いやすい設計
  • TypeScript対応: 型の補完が効く
  • 日本語対応: 日本語表示もバッチリ

週間ダウンロード数は約200万回という、信頼性の高いライブラリです。

インストール方法

まず、プロジェクトにライブラリをインストールしましょう。

npm install react-datepicker

これだけで準備完了です! 簡単ですよね。

基本的なセットアップ

次に、基本的な使い方を見てみましょう。

import React, { useState } from 'react';
import DatePicker from 'react-datepicker';

// CSSファイルも忘れずにインポート
import 'react-datepicker/dist/react-datepicker.css';

const BasicDatePicker = () => {
  const [selectedDate, setSelectedDate] = useState(new Date());
  
  return (
    <div>
      <h3>日付を選択してください</h3>
      <DatePicker
        selected={selectedDate}
        onChange={(date) => setSelectedDate(date)}
      />
      <p>選択された日付: {selectedDate?.toLocaleDateString('ja-JP')}</p>
    </div>
  );
};

export default BasicDatePicker;

このコードの動作を確認してみましょう。

まず、必要なライブラリをインポートします。 DatePickerがメインのコンポーネントで、CSSファイルも忘れずに読み込みます。

useStateで現在選択されている日付を管理しています。 初期値はnew Date()で今日の日付を設定しています。

DatePickerコンポーネントには2つのプロパティを設定しています。 selectedで現在選択されている日付を表示し、onChangeで日付が変更されたときの処理を定義しています。

最後に、選択された日付を日本語形式で表示しています。

「意外と簡単!」と思いませんか? この基本形を覚えれば、あとは機能を追加していくだけです。

基本的な実装パターン

実際のプロジェクトでよく使われるパターンを見てみましょう。

パターン1: シンプルな日付選択

最もシンプルな日付選択の実装です。

const SimpleDatePicker = () => {
  const [startDate, setStartDate] = useState(new Date());
  
  return (
    <div className="date-picker-container">
      <label htmlFor="date-input">日付選択:</label>
      <DatePicker
        id="date-input"
        selected={startDate}
        onChange={(date) => setStartDate(date)}
        dateFormat="yyyy/MM/dd"
        placeholderText="日付を選択してください"
      />
    </div>
  );
};

このコードでは、より実用的な設定を追加しています。

label要素で入力欄の説明を追加し、htmlForidで関連付けています。 これにより、アクセシビリティが向上します。

dateFormatで日付の表示形式を指定しています。 yyyy/MM/ddで「2024/01/15」のような形式になります。

placeholderTextで、何も選択されていないときのヒントテキストを設定しています。

パターン2: 期間選択(開始日〜終了日)

予約システムや検索機能でよく使われる期間選択です。

const DateRangePicker = () => {
  const [startDate, setStartDate] = useState(new Date());
  const [endDate, setEndDate] = useState(null);
  
  return (
    <div className="date-range-container">
      <div className="date-input-group">
        <label>開始日:</label>
        <DatePicker
          selected={startDate}
          onChange={(date) => setStartDate(date)}
          selectsStart
          startDate={startDate}
          endDate={endDate}
          placeholderText="開始日を選択"
        />
      </div>
      
      <div className="date-input-group">
        <label>終了日:</label>
        <DatePicker
          selected={endDate}
          onChange={(date) => setEndDate(date)}
          selectsEnd
          startDate={startDate}
          endDate={endDate}
          minDate={startDate}
          placeholderText="終了日を選択"
        />
      </div>
      
      {startDate && endDate && (
        <p>
          選択期間: {startDate.toLocaleDateString('ja-JP')} 
          ~ {endDate.toLocaleDateString('ja-JP')}
        </p>
      )}
    </div>
  );
};

期間選択では、2つの日付を管理する必要があります。 startDateendDateという2つのstateを用意しています。

開始日の設定では、selectsStartプロパティを使います。 これにより、期間選択の開始日として動作します。

終了日の設定では、selectsEndプロパティを使います。 さらに、minDate={startDate}で、開始日より前の日付を選択できないようにしています。

最後に、両方の日付が選択されているときだけ、選択期間を表示しています。

パターン3: 時刻選択付き

日時を両方選択したい場合の実装です。

const DateTimePicker = () => {
  const [selectedDateTime, setSelectedDateTime] = useState(new Date());
  
  return (
    <div>
      <label>日時選択:</label>
      <DatePicker
        selected={selectedDateTime}
        onChange={(date) => setSelectedDateTime(date)}
        showTimeSelect
        timeFormat="HH:mm"
        timeIntervals={15}
        timeCaption="時刻"
        dateFormat="yyyy/MM/dd HH:mm"
        placeholderText="日時を選択してください"
      />
      
      <p>
        選択された日時: {selectedDateTime?.toLocaleString('ja-JP')}
      </p>
    </div>
  );
};

時刻選択を有効にするには、showTimeSelectプロパティを追加します。

timeFormatで時刻の表示形式を指定しています。 HH:mmで24時間形式の「14:30」のような表示になります。

timeIntervalsで時刻の選択間隔を指定します。 15なら15分刻みで選択できます。

timeCaptionで時刻選択部分のラベルを設定しています。

dateFormatを変更して、日時両方を表示するようにしています。

「結構簡単に色々な機能が付けられるんですね!」 そうなんです。基本的な使い方を覚えれば、あとは設定を変えるだけで様々な機能を追加できます。

日本語化とローカライゼーション

日本のユーザーにとって使いやすくするための設定を見てみましょう。

日本語表示の設定

カレンダーの月名や曜日を日本語で表示する方法です。

import { registerLocale, setDefaultLocale } from 'react-datepicker';
import ja from 'date-fns/locale/ja';

// 日本語ロケールを登録
registerLocale('ja', ja);

const JapaneseDatePicker = () => {
  const [selectedDate, setSelectedDate] = useState(new Date());
  
  return (
    <DatePicker
      selected={selectedDate}
      onChange={(date) => setSelectedDate(date)}
      locale="ja"  // 日本語に設定
      dateFormat="yyyy年MM月dd日"
      placeholderText="日付を選択してください"
    />
  );
};

まず、日本語ロケールを使うための準備をします。 registerLocaleで日本語設定を登録しています。

locale="ja"を設定することで、カレンダーの表示が日本語になります。 月名が「1月」「2月」のようになり、曜日も「日」「月」のように表示されます。

dateFormatも日本語に合わせて「yyyy年MM月dd日」の形式に変更しています。

カスタム日本語ラベル

さらに細かい日本語設定をする場合の例です。

const CustomJapaneseDatePicker = () => {
  const [selectedDate, setSelectedDate] = useState(new Date());
  
  return (
    <DatePicker
      selected={selectedDate}
      onChange={(date) => setSelectedDate(date)}
      locale="ja"
      dateFormat="yyyy年MM月dd日"
      
      // 年・月の選択ドロップダウンを表示
      showMonthDropdown
      showYearDropdown
      yearDropdownItemNumber={10}
      scrollableYearDropdown
      
      // 日本語のプレースホルダー
      placeholderText="年月日を入力または選択"
      
      // 今日ボタンの日本語化
      todayButton="今日"
    />
  );
};

この設定では、より使いやすい日本語インターフェースを実現しています。

showMonthDropdownshowYearDropdownで、年と月をドロップダウンで選択できるようになります。 遠い過去や未来の日付を選ぶときに便利です。

yearDropdownItemNumberで、年のドロップダウンに表示する項目数を指定しています。

todayButton="今日"で、今日の日付に戻るボタンを日本語で表示しています。

「これで日本のユーザーにとって使いやすくなりますね!」 そうですね。ローカライゼーションは、ユーザー体験を大きく向上させる重要な要素です。

実用的なカスタマイズ

実際のプロジェクトで使える、より高度なカスタマイズを見てみましょう。

バリデーション付きフォーム

日付の妥当性をチェックするフォームの実装です。

const ValidatedDateForm = () => {
  const [birthDate, setBirthDate] = useState(null);
  const [errors, setErrors] = useState({});
  
  const validateDate = (date) => {
    const newErrors = {};
    
    if (!date) {
      newErrors.birthDate = '生年月日を選択してください';
    } else {
      const today = new Date();
      const age = today.getFullYear() - date.getFullYear();
      
      if (date > today) {
        newErrors.birthDate = '未来の日付は選択できません';
      } else if (age > 120) {
        newErrors.birthDate = '正しい生年月日を入力してください';
      }
    }
    
    setErrors(newErrors);
    return Object.keys(newErrors).length === 0;
  };
  
  const handleSubmit = (e) => {
    e.preventDefault();
    
    if (validateDate(birthDate)) {
      console.log('送信成功:', birthDate);
      alert('登録が完了しました');
    }
  };
  
  return (
    <form onSubmit={handleSubmit} className="date-form">
      <div className="form-group">
        <label htmlFor="birth-date">生年月日 *</label>
        <DatePicker
          id="birth-date"
          selected={birthDate}
          onChange={(date) => {
            setBirthDate(date);
            validateDate(date);
          }}
          locale="ja"
          dateFormat="yyyy年MM月dd日"
          showMonthDropdown
          showYearDropdown
          yearDropdownItemNumber={80}
          scrollableYearDropdown
          placeholderText="生年月日を選択"
          maxDate={new Date()}  // 今日以前のみ選択可能
          
          // エラー時のスタイル
          className={errors.birthDate ? 'error' : ''}
        />
        
        {errors.birthDate && (
          <span className="error-message">{errors.birthDate}</span>
        )}
      </div>
      
      <button type="submit" className="submit-button">
        登録
      </button>
    </form>
  );
};

このフォームでは、しっかりとしたバリデーションを実装しています。

validateDate関数で、日付の妥当性をチェックしています。 未選択、未来の日付、120歳を超える年齢などをエラーとして検出します。

maxDate={new Date()}で、今日以前の日付のみ選択可能にしています。 これにより、未来の日付を選択できないようになります。

エラーがある場合は、エラーメッセージを表示し、入力欄にエラーのスタイルを適用します。

「フォームのバリデーションも簡単に実装できるんですね!」 そうです。適切なバリデーションは、ユーザー体験を大きく向上させます。

営業日のみ選択可能なカレンダー

営業日のみを選択できるカレンダーの実装です。

const BusinessDayPicker = () => {
  const [selectedDate, setSelectedDate] = useState(null);
  
  // 営業日かどうかを判定する関数
  const isBusinessDay = (date) => {
    const day = date.getDay();
    // 土曜日(6)、日曜日(0)を除外
    return day !== 0 && day !== 6;
  };
  
  // 祝日リスト(簡易版)
  const holidays = [
    new Date(2024, 0, 1),   // 元日
    new Date(2024, 4, 3),   // 憲法記念日
    new Date(2024, 4, 4),   // みどりの日
    new Date(2024, 4, 5),   // こどもの日
    // 他の祝日も追加可能
  ];
  
  const isHoliday = (date) => {
    return holidays.some(holiday => 
      holiday.toDateString() === date.toDateString()
    );
  };
  
  const isSelectableDate = (date) => {
    return isBusinessDay(date) && !isHoliday(date);
  };
  
  return (
    <div>
      <h3>配送日選択(営業日のみ)</h3>
      <DatePicker
        selected={selectedDate}
        onChange={(date) => setSelectedDate(date)}
        locale="ja"
        dateFormat="yyyy年MM月dd日(eee)"
        minDate={new Date()}  // 今日以降のみ
        filterDate={isSelectableDate}  // 営業日のみ選択可能
        placeholderText="配送日を選択してください"
        
        // 土日祝日を強調表示
        dayClassName={(date) => {
          if (!isSelectableDate(date)) {
            return 'unavailable-day';
          }
          return undefined;
        }}
      />
      
      {selectedDate && (
        <p>
          配送予定日: {selectedDate.toLocaleDateString('ja-JP')}
          ({selectedDate.toLocaleDateString('ja-JP', { weekday: 'long' })})
        </p>
      )}
    </div>
  );
};

この実装では、営業日のみを選択可能にしています。

isBusinessDay関数で、土日を除外する判定を行っています。 date.getDay()で曜日を取得し、0(日曜)と6(土曜)を除外しています。

holidays配列で祝日を定義し、isHoliday関数で祝日判定を行っています。

filterDateプロパティで、選択可能な日付を制限しています。 営業日かつ祝日でない日付のみが選択可能になります。

dayClassNameで、選択不可な日付に特別なスタイルを適用しています。

「これで営業日のみの配送日選択が実現できますね!」 はい。このような業務要件に応じたカスタマイズも簡単に実装できます。

高度な実装例

さらに実用的な機能を組み合わせた例を見てみましょう。

予約システムの例

予約システムでよく使われる、日時と時間枠を組み合わせた実装です。

const ReservationDatePicker = () => {
  const [reservationDate, setReservationDate] = useState(null);
  const [timeSlot, setTimeSlot] = useState('');
  const [availableSlots, setAvailableSlots] = useState([]);
  
  // 利用可能な時間枠
  const timeSlots = [
    '09:00', '10:00', '11:00', '13:00', 
    '14:00', '15:00', '16:00', '17:00'
  ];
  
  // 予約済み時間枠(実際はAPIから取得)
  const bookedSlots = {
    '2024-01-15': ['09:00', '14:00'],
    '2024-01-16': ['10:00', '11:00', '15:00']
  };
  
  const handleDateChange = (date) => {
    setReservationDate(date);
    setTimeSlot('');
    
    if (date) {
      const dateString = date.toISOString().split('T')[0];
      const booked = bookedSlots[dateString] || [];
      const available = timeSlots.filter(slot => !booked.includes(slot));
      setAvailableSlots(available);
    }
  };
  
  const handleReservation = () => {
    if (reservationDate && timeSlot) {
      const reservationInfo = {
        date: reservationDate.toLocaleDateString('ja-JP'),
        time: timeSlot
      };
      
      console.log('予約情報:', reservationInfo);
      alert(`予約を受け付けました
日時: ${reservationInfo.date} ${reservationInfo.time}`);
    }
  };
  
  return (
    <div className="reservation-container">
      <h3>予約日時選択</h3>
      
      <div className="date-selection">
        <label>予約日:</label>
        <DatePicker
          selected={reservationDate}
          onChange={handleDateChange}
          locale="ja"
          dateFormat="yyyy年MM月dd日(eee)"
          minDate={new Date()}
          maxDate={new Date(Date.now() + 30 * 24 * 60 * 60 * 1000)} // 30日後まで
          placeholderText="予約日を選択"
          
          // 土日を除外
          filterDate={(date) => {
            const day = date.getDay();
            return day !== 0 && day !== 6;
          }}
        />
      </div>
      
      {reservationDate && (
        <div className="time-selection">
          <label>時間枠:</label>
          <select 
            value={timeSlot} 
            onChange={(e) => setTimeSlot(e.target.value)}
            className="time-select"
          >
            <option value="">時間を選択してください</option>
            {availableSlots.map(slot => (
              <option key={slot} value={slot}>
                {slot}
              </option>
            ))}
          </select>
          
          {availableSlots.length === 0 && (
            <p className="no-slots">この日は予約枠がありません</p>
          )}
        </div>
      )}
      
      <button 
        onClick={handleReservation}
        disabled={!reservationDate || !timeSlot}
        className="reservation-button"
      >
        予約する
      </button>
    </div>
  );
};

この予約システムでは、複数の機能を組み合わせています。

日付が選択されると、handleDateChange関数が実行されます。 選択された日付に応じて、利用可能な時間枠を計算し、availableSlotsを更新します。

bookedSlotsで予約済みの時間枠を管理しています。 実際のプロジェクトでは、APIから取得することが多いでしょう。

時間枠の選択にはselect要素を使用し、利用可能な時間のみを表示しています。

「予約システムみたいな複雑な機能も作れるんですね!」 そうです。基本的な機能を組み合わせることで、実用的なシステムを構築できます。

検索フィルターでの期間指定

検索システムでよく使われる期間指定フィルターの実装です。

const SearchWithDateRange = () => {
  const [searchParams, setSearchParams] = useState({
    startDate: null,
    endDate: null,
    keyword: ''
  });
  const [searchResults, setSearchResults] = useState([]);
  
  const handleSearch = async () => {
    // 検索パラメータの構築
    const params = {
      keyword: searchParams.keyword,
      startDate: searchParams.startDate?.toISOString().split('T')[0],
      endDate: searchParams.endDate?.toISOString().split('T')[0]
    };
    
    console.log('検索パラメータ:', params);
    
    // 実際の検索処理(APIコール等)
    // const results = await searchAPI(params);
    // setSearchResults(results);
    
    // デモ用の結果
    setSearchResults([
      { id: 1, title: '検索結果1', date: '2024-01-15' },
      { id: 2, title: '検索結果2', date: '2024-01-20' }
    ]);
  };
  
  const clearDateRange = () => {
    setSearchParams(prev => ({
      ...prev,
      startDate: null,
      endDate: null
    }));
  };
  
  return (
    <div className="search-container">
      <h3>期間指定検索</h3>
      
      <div className="search-form">
        <div className="keyword-input">
          <label>キーワード:</label>
          <input
            type="text"
            value={searchParams.keyword}
            onChange={(e) => setSearchParams(prev => ({
              ...prev,
              keyword: e.target.value
            }))}
            placeholder="検索キーワード"
          />
        </div>
        
        <div className="date-range">
          <label>期間:</label>
          <div className="date-inputs">
            <DatePicker
              selected={searchParams.startDate}
              onChange={(date) => setSearchParams(prev => ({
                ...prev,
                startDate: date
              }))}
              locale="ja"
              dateFormat="yyyy/MM/dd"
              placeholderText="開始日"
              selectsStart
              startDate={searchParams.startDate}
              endDate={searchParams.endDate}
            />
            
            <span className="date-separator">〜</span>
            
            <DatePicker
              selected={searchParams.endDate}
              onChange={(date) => setSearchParams(prev => ({
                ...prev,
                endDate: date
              }))}
              locale="ja"
              dateFormat="yyyy/MM/dd"
              placeholderText="終了日"
              selectsEnd
              startDate={searchParams.startDate}
              endDate={searchParams.endDate}
              minDate={searchParams.startDate}
            />
          </div>
          
          <button onClick={clearDateRange} className="clear-button">
            期間クリア
          </button>
        </div>
        
        <button onClick={handleSearch} className="search-button">
          検索
        </button>
      </div>
      
      <div className="search-results">
        {searchResults.map(result => (
          <div key={result.id} className="result-item">
            <h4>{result.title}</h4>
            <p>日付: {result.date}</p>
          </div>
        ))}
      </div>
    </div>
  );
};

この検索システムでは、キーワードと期間を組み合わせた検索が可能です。

searchParamsというオブジェクトで、すべての検索条件を一元管理しています。

handleSearch関数で、検索パラメータを構築し、実際の検索処理を行います。 日付はISO形式の文字列に変換して、APIに送信することが多いでしょう。

clearDateRange関数で、期間指定のみをクリアできるようにしています。

「検索システムにも簡単に期間指定機能を追加できるんですね!」 はい。このような実用的な機能も、基本的な使い方の組み合わせで実現できます。

スタイリングとカスタマイズ

見た目をカスタマイズする方法を見てみましょう。

基本的なCSS設定

React DatePickerの外観を調整するCSSの例です。

/* react-datepicker のカスタマイズ */
.react-datepicker-wrapper {
  width: 100%;
}

.react-datepicker__input-container input {
  width: 100%;
  padding: 8px 12px;
  border: 1px solid #ddd;
  border-radius: 4px;
  font-size: 14px;
}

.react-datepicker__input-container input:focus {
  outline: none;
  border-color: #007bff;
  box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25);
}

/* 選択不可の日付 */
.unavailable-day {
  color: #ccc !important;
  background-color: #f5f5f5 !important;
}

/* エラー時のスタイル */
.error {
  border-color: #dc3545 !important;
}

.error-message {
  color: #dc3545;
  font-size: 12px;
  margin-top: 4px;
  display: block;
}

/* フォームスタイル */
.date-form .form-group {
  margin-bottom: 20px;
}

.date-form label {
  display: block;
  margin-bottom: 5px;
  font-weight: bold;
}

.submit-button {
  background-color: #007bff;
  color: white;
  padding: 10px 20px;
  border: none;
  border-radius: 4px;
  cursor: pointer;
}

.submit-button:disabled {
  background-color: #6c757d;
  cursor: not-allowed;
}

このCSSでは、React DatePickerの基本的な見た目を整えています。

入力欄のスタイルを統一し、フォーカス時のハイライトを追加しています。 選択不可な日付は、グレーアウト表示にしています。

エラー時の赤い枠線と、エラーメッセージのスタイルも定義しています。

「CSSでしっかりとスタイルを整えることで、プロフェッショナルな見た目になりますね!」 その通りです。適切なスタイリングは、ユーザー体験を大きく向上させます。

まとめ

React DatePickerを使った日付選択UIの実装について、たくさんのパターンを見てきました。

重要なポイント

これらのポイントを押さえておきましょう。

  • 基本実装: selectedonChangeの基本プロパティを理解する
  • 日本語化: locale設定で日本語表示に対応する
  • バリデーション: 適切な制限とエラーハンドリングを実装する
  • カスタマイズ: 業務要件に応じた機能拡張を行う
  • スタイリング: CSSでデザインを統一する

実装時のコツ

実装を成功させるためのコツをお伝えします。

  • 必要な機能から段階的に実装する
  • ユーザビリティを重視した設計にする
  • レスポンシブ対応とアクセシビリティを考慮する
  • 適切なバリデーションでユーザー体験を向上させる

React DatePickerは柔軟性が高く実用的なライブラリです。 基本的な使い方から始めて、要件に応じて機能を拡張していくことで、高品質な日付選択UIを効率的に実装できます。

「これで日付選択機能が作れそうです!」 そうですね。まずは基本的な実装から始めて、少しずつ機能を追加していってください。

ぜひこの記事を参考に、あなたのプロジェクトに最適な日付選択機能を実装してみてください!

関連記事