diff --git a/redirect_bulk.links.action.yml b/redirect_bulk.links.action.yml
index 3ba8e904f97eb8710a8adb229c5d311e38a05494..2cf8a6ffdba167854c961beca1e1ad750c67ff7d 100644
--- a/redirect_bulk.links.action.yml
+++ b/redirect_bulk.links.action.yml
@@ -3,3 +3,10 @@ redirect_bulk.add_bulk_redirect:
   title: 'Add Bulk redirects'
   appears_on:
     - 'redirect.list'
+
+redirect_bulk.add_csv_redirect:
+  route_name: redirect_bulk.csv_form
+  title: 'Import CSV'
+  appears_on:
+    - 'redirect.list'
+    
\ No newline at end of file
diff --git a/redirect_bulk.routing.yml b/redirect_bulk.routing.yml
index c17b9707930a6ca217edbc2d0dc5f137077c16e9..b2221b38e2551fbaccdb04d6eb2dac6b6fcbc34f 100644
--- a/redirect_bulk.routing.yml
+++ b/redirect_bulk.routing.yml
@@ -13,3 +13,11 @@ redirect_bulk.node_autocomplete:
     _title: 'Node Autocomplete'
   requirements:
     _permission: 'access content'        
+
+redirect_bulk.csv_form:
+  path: '/admin/config/search/redirect/add-csv'
+  defaults:
+    _form: '\Drupal\redirect_bulk\Form\CsvForm'
+    _title: 'Import csv'
+  requirements:
+    _permission: 'administer bulk redirects'
diff --git a/src/Form/CsvForm.php b/src/Form/CsvForm.php
new file mode 100644
index 0000000000000000000000000000000000000000..c14e7f17cc1b0eff3a0401dabc10f44eb6bfcc57
--- /dev/null
+++ b/src/Form/CsvForm.php
@@ -0,0 +1,222 @@
+<?php
+
+namespace Drupal\redirect_bulk\Form;
+
+use Drupal\Component\Utility\UrlHelper;
+use Drupal\Core\Config\ConfigFactoryInterface;
+use Drupal\Core\File\FileSystemInterface;
+use Drupal\Core\Form\FormBase;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Messenger\MessengerInterface;
+use Drupal\redirect\Entity\Redirect;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Provides a form to import csv.
+ */
+class CsvForm extends FormBase {
+
+  /**
+   * Config factory service.
+   *
+   * @var \Drupal\Core\Config\ConfigFactoryInterface
+   */
+  protected $configFactory;
+
+  /**
+   * Messenger service.
+   *
+   * @var \Drupal\Core\Messenger\MessengerInterface
+   */
+  protected $messenger;
+
+  /**
+   * Constructs a new CsvForm.
+   */
+  public function __construct(ConfigFactoryInterface $config_factory, MessengerInterface $messenger) {
+    $this->configFactory = $config_factory;
+    $this->messenger = $messenger;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static(
+      $container->get('config.factory'),
+      $container->get('messenger')
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFormId() {
+    return 'redirect_bulk_import_csv';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildForm(array $form, FormStateInterface $form_state) {
+    $form['import'] = [
+      '#type' => 'file',
+      '#title' => $this->t('Import CSV'),
+      '#description' => $this->t('Upload a CSV file with redirects. Format: from, to, code, langcode (langcode is optional).'),
+      '#required' => TRUE,
+    ];
+
+    $form['actions']['#type'] = 'actions';
+    $form['actions']['submit'] = [
+      '#type' => 'submit',
+      '#value' => $this->t('Import'),
+    ];
+
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitForm(array &$form, FormStateInterface $form_state) {
+    $validators = ['file_validate_extensions' => ['csv']];
+    $file = file_save_upload('import', $validators, FALSE, 0, FileSystemInterface::EXISTS_REPLACE);
+
+    if ($file) {
+      $file->setPermanent();
+      $file->save();
+      $file_path = $file->getFileUri();
+      $content = file_get_contents($file_path);
+      $redirects = $this->parseCsv($content);
+
+      if (empty($redirects)) {
+        $this->messenger->addError($this->t('The uploaded CSV file is empty or invalid.'));
+        return;
+      }
+
+      $success_count = 0;
+      $error_count = 0;
+
+      foreach ($redirects as $redirect) {
+        if ($this->validateRedirect($redirect)) {
+          $to = $this->clearUrl($redirect['to']);
+          if (!UrlHelper::isExternal($to)) {
+            $to = 'internal:/' . ltrim($to, '/');
+          }
+
+          $source = UrlHelper::parse($redirect['from']);
+          $path = $this->clearUrl($source['path']);
+          $query = $source['query'] ?? [];
+          $status_code = $redirect['code'];
+          $langcode = $redirect['langcode'];
+          $default_code = $this->configFactory->get('redirect.settings')->get('default_status_code') ?? 301;
+
+          Redirect::create([
+            'redirect_source' => [
+              'path' => $path,
+              'query' => $query,
+            ],
+            'redirect_redirect' => [
+              'uri' => $to,
+              'title' => '',
+            ],
+            'status_code' => (!empty($status_code) && is_numeric($status_code)) ? (int) trim($status_code) : $default_code,
+            'language' => !empty($langcode) ? $langcode : 'und',
+          ])->save();
+
+          $success_count++;
+        }
+        else {
+          $error_count++;
+        }
+      }
+      if ($success_count > 0) {
+        $this->messenger->addStatus($this->t('@count redirects successfully imported.', ['@count' => $success_count]));
+      }
+      if ($error_count > 0) {
+        $this->messenger->addWarning($this->t('@count redirects could not be processed. Please ensure all entries follow the correct format.', ['@count' => $error_count]));
+      }
+    }
+    else {
+      $this->messenger->addError($this->t('Failed to upload the file.'));
+    }
+  }
+
+  /**
+   * Parses CSV content into an array of redirects.
+   */
+  private function parseCsv(string $content): array {
+    $lines = array_filter(explode("\n", $content));
+    $redirects = [];
+
+    foreach ($lines as $line) {
+      $data = str_getcsv($line);
+      if (count($data) >= 2) {
+
+        $redirects[] = [
+          'from' => isset($data[0]) ? trim($data[0]) : '',
+          'to' => isset($data[1]) ? trim($data[1]) : '',
+          'code' => isset($data[2]) ? trim($data[2]) : '',
+          'langcode' => isset($data[3]) ? trim($data[3]) : '',
+        ];
+      }
+    }
+    return $redirects;
+  }
+
+  /**
+   * Validates a single redirect entry.
+   */
+  private function validateRedirect(array $redirect): bool {
+    $from = $this->clearUrl($redirect['from']);
+    $to = $this->clearUrl($redirect['to']);
+    $langcode = !empty($redirect['langcode']) ? $redirect['langcode'] : 'und';
+    $source = UrlHelper::parse($redirect['from']);
+    $path = $this->clearUrl($source['path']);
+    $query = $source['query'];
+    $available_languages = \Drupal::languageManager()->getLanguages();
+
+    $hash = Redirect::generateHash($path, $query, $langcode);
+    $existing_redirects = \Drupal::entityTypeManager()
+      ->getStorage('redirect')
+      ->loadByProperties(['hash' => $hash]);
+
+    if (!empty($existing_redirects)) {
+      $existing_redirect = reset($existing_redirects);
+      $this->messenger->addError($this->t('The source path %source is already being redirected. Edit the existing redirect <a href="@edit-url">here</a>.', [
+        '%source' => $path,
+        '@edit-url' => $existing_redirect->toUrl('edit-form')->toString(),
+      ]));
+      return FALSE;
+    }
+
+    if (empty($from) || empty($to)) {
+      $this->messenger->addError($this->t('Source and destination paths cannot be empty.'));
+      return FALSE;
+    }
+
+    if ($from === $to) {
+      $this->messenger->addError($this->t('Source and destination paths cannot be the same.'));
+      return FALSE;
+    }
+
+    if (!empty($redirect['code']) && !in_array((int) $redirect['code'], range(300, 307), TRUE)) {
+      $this->messenger->addError($this->t('Invalid redirect code: @code', ['@code' => $redirect['code']]));
+      return FALSE;
+    }
+
+    if (!empty($redirect['langcode']) && !array_key_exists($langcode, $available_languages)) {
+      $this->messenger->addError($this->t('Invalid language code: @code', ['@code' => $redirect['langcode']]));
+      return FALSE;
+    }
+    return TRUE;
+  }
+
+  /**
+   * Clears and sanitizes the URL.
+   */
+  private function clearUrl(string $url): string {
+    return ltrim(trim($url), '/');
+  }
+
+}