-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathTutorial v6.4.3 _ React Router.html
1255 lines (1252 loc) · 321 KB
/
Tutorial v6.4.3 _ React Router.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<!DOCTYPE html>
<!-- saved from url=(0046)https://reactrouter.com/en/main/start/tutorial -->
<html lang="en" class=""><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><script>
let colorScheme = "system";
if (colorScheme === "system") {
let media = window.matchMedia("(prefers-color-scheme: dark)")
if (media.matches) document.documentElement.classList.add("dark");
}
</script><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="icon" href="https://reactrouter.com/favicon-light.png" type="image/png" media="(prefers-color-scheme: light)"><link rel="icon" href="https://reactrouter.com/favicon-dark.png" type="image/png" media="(prefers-color-scheme: dark)"><title>Tutorial v6.4.3 | React Router</title><meta content="index,follow" name="robots"><meta content="index,follow" name="googlebot"><meta content="Tutorial v6.4.3" name="twitter:title"><meta content="summary" name="twitter:card"><meta content="@remix_run" name="twitter:site"><meta content="@remix_run" name="twitter:creator"><meta content="https://reactrouter.com/twitterimage.jpg" name="twitter:image"><meta content="React Router logo" name="twitter:image:alt"><meta content="Tutorial v6.4.3" property="og:title"><meta content="https://reactrouter.com/ogimage.png" property="og:image"><meta content="React Router logo" property="og:image:alt"><meta content="1200" property="og:image:width"><meta content="627" property="og:image:height"><link rel="stylesheet" href="./Tutorial v6.4.3 _ React Router_files/styles.processed-2TTMDA4M.css"></head><body class="bg-white text-black dark:bg-gray-900 dark:text-white"><img src="./Tutorial v6.4.3 _ React Router_files/icons-UIZOAPCC.svg" alt="" hidden="" fetchpriority="high"><div class="lg:m-auto lg:max-w-[90rem]"><div class="sticky top-0 z-20"><div class="relative z-20 flex h-16 w-full items-center justify-between border-b border-gray-50 bg-white px-4 py-3 text-gray-900 dark:border-gray-800 dark:bg-gray-900 dark:text-gray-100 lg:px-8"><div class="flex w-full items-center justify-between gap-8 md:w-auto"><a class="flex items-center gap-1 text-gray-900 dark:text-white" href="https://reactrouter.com/en/main"><svg aria-label="React Router logo, nine dots in an upward triangle (one on top, two in the middle, three on the bottom) with a path of three highlighted and connected from top to bottom" class="h-14 w-14 md:h-12 md:w-12"><use href="/build/_assets/icons-UIZOAPCC.svg#logo"></use></svg><div class="hidden md:block"><svg aria-label="React Router" class="h-6 w-40"><use href="/build/_assets/icons-UIZOAPCC.svg#logotype"></use></svg></div></a><div class="flex items-center gap-2"><details class="relative"><summary class="_no-triangle relative flex h-[40px] cursor-pointer list-none items-center justify-center gap-3 rounded-full px-3 border border-transparent bg-gray-100 hover:bg-gray-200 focus:border focus:border-gray-100 focus:bg-white dark:bg-gray-800 dark:hover:bg-gray-700 dark:focus:border-gray-400 dark:focus:bg-gray-700"><div>main</div><svg aria-hidden="true" class="h-[18px] w-[18px] text-gray-400"><use href="/build/_assets/icons-UIZOAPCC.svg#dropdown-arrows"></use></svg></summary><div class="absolute right-0 z-20 md:left-0"><div class="relative top-1 w-40 rounded-lg border border-gray-100 bg-white py-2 shadow-lg dark:border-gray-400 dark:bg-gray-800 "><div class="px-4 pt-2 pb-2 text-xs font-bold uppercase tracking-wider text-gray-400 dark:text-gray-300">Branches</div><span class="relative pl-4 group items-center flex py-1 before:mr-4 before:relative before:top-px before:block before:h-1.5 before:w-1.5 before:rounded-full before:content-[''] font-bold text-red-brand before:bg-red-brand">main (6.4.3)</span><a class="relative pl-4 group items-center flex py-1 before:mr-4 before:relative before:top-px before:block before:h-1.5 before:w-1.5 before:rounded-full before:content-[''] before:bg-transparent hover:bg-gray-50 active:text-red-brand dark:text-gray-200 dark:hover:bg-gray-700 dark:active:text-red-brand" href="https://reactrouter.com/en/dev">dev</a><div class="px-4 pt-2 pb-2 text-xs font-bold uppercase tracking-wider text-gray-400 dark:text-gray-300">Versions</div><a class="relative pl-4 group items-center flex py-1 before:mr-4 before:relative before:top-px before:block before:h-1.5 before:w-1.5 before:rounded-full before:content-[''] before:bg-transparent hover:bg-gray-50 active:text-red-brand dark:text-gray-200 dark:hover:bg-gray-700 dark:active:text-red-brand" href="https://reactrouter.com/en/6.4.3">6.4.3</a><a href="https://v5.reactrouter.com/" class="relative pl-4 group items-center flex py-1 before:mr-4 before:relative before:top-px before:block before:h-1.5 before:w-1.5 before:rounded-full before:content-[''] after:absolute after:right-4 after:top-1 after:block after:-rotate-45 after:opacity-50 after:content-['→'] hover:bg-gray-50 active:text-red-brand dark:text-gray-200 dark:hover:bg-gray-700 dark:active:text-red-brand">v4/5.x</a><a href="https://github.com/remix-run/react-router/tree/v3.2.6/docs" class="relative pl-4 group items-center flex py-1 before:mr-4 before:relative before:top-px before:block before:h-1.5 before:w-1.5 before:rounded-full before:content-[''] after:absolute after:right-4 after:top-1 after:block after:-rotate-45 after:opacity-50 after:content-['→'] hover:bg-gray-50 active:text-red-brand dark:text-gray-200 dark:hover:bg-gray-700 dark:active:text-red-brand">v3.x</a></div></div></details><details class="relative cursor-pointer"><summary class="_no-triangle focus:border-200 flex h-[40px] w-[40px] items-center justify-center rounded-full border border-transparent bg-gray-100 hover:bg-gray-200 focus:border focus:border-gray-100 focus:bg-white dark:bg-gray-800 dark:hover:bg-gray-700 dark:focus:border-gray-400 dark:focus:bg-gray-700"><svg class="hidden h-[24px] w-[24px] dark:inline"><use href="/build/_assets/icons-UIZOAPCC.svg#moon"></use></svg><svg class="h-[24px] w-[24px] dark:hidden"><use href="/build/_assets/icons-UIZOAPCC.svg#sun"></use></svg></summary><div class="absolute right-0 z-20 md:left-0"><div class="relative top-1 w-40 rounded-lg border border-gray-100 bg-white py-2 shadow-lg dark:border-gray-400 dark:bg-gray-800 "><form method="post" action="https://reactrouter.com/color-scheme" enctype="application/x-www-form-urlencoded"><input type="hidden" name="returnTo" value="/en/main/start/tutorial"><button value="light" name="colorScheme" class="flex w-full items-center gap-4 py-1 px-4 text-red-brand" disabled=""><svg class="h-[18px] w-[18px]"><use href="/build/_assets/icons-UIZOAPCC.svg#sun"></use></svg> <!-- -->Light</button><button value="dark" name="colorScheme" class="flex w-full items-center gap-4 py-1 px-4 hover:bg-gray-50 active:text-red-brand dark:hover:bg-gray-700 dark:active:text-red-brand"><svg class="h-[18px] w-[18px]"><use href="/build/_assets/icons-UIZOAPCC.svg#moon"></use></svg> <!-- -->Dark</button><button value="system" name="colorScheme" class="flex w-full items-center gap-4 py-1 px-4 hover:bg-gray-50 active:text-red-brand dark:hover:bg-gray-700 dark:active:text-red-brand"><svg class="h-[18px] w-[18px]"><use href="/build/_assets/icons-UIZOAPCC.svg#setting"></use></svg> <!-- -->System</button></form></div></div></details></div></div><div class="flex items-center gap-4"><a href="https://github.com/remix-run/react-router" class="hidden text-gray-400 hover:text-gray-900 dark:text-gray-400 dark:hover:text-gray-50 md:block" title="View code on GitHub"><svg aria-label="GitHub octocat logo in a circle" style="width:24px;height:24px"><use href="/build/_assets/icons-UIZOAPCC.svg#github"></use></svg></a><a href="https://rmx.as/discord" class="hidden text-gray-400 hover:text-gray-900 dark:text-gray-400 dark:hover:text-gray-50 md:block" title="Chat on Discord"><svg aria-label="Discord logo in a circle" style="width:24px;height:24px"><use href="/build/_assets/icons-UIZOAPCC.svg#discord"></use></svg></a><a href="https://remix.run/" class="hidden text-gray-400 hover:text-gray-900 dark:text-gray-400 dark:hover:text-gray-50 md:block"><svg aria-label="Stylized text saying “Made by Remix” with an right pointing arrow." style="width:122px;height:17px"><use href="/build/_assets/icons-UIZOAPCC.svg#remix"></use></svg></a></div></div><details class="group relative flex h-full flex-col lg:hidden"><summary tabindex="0" class="_no-triangle flex cursor-pointer select-none items-center gap-2 border-b border-gray-50 bg-white px-2 py-3 text-sm font-medium hover:bg-gray-50 active:bg-gray-100 dark:border-gray-700 dark:bg-gray-900 dark:hover:bg-gray-800 dark:active:bg-gray-700"><div class="flex items-center gap-2"><svg aria-hidden="true" class="h-5 w-5 group-open:hidden"><use href="/build/_assets/icons-UIZOAPCC.svg#chevron-r"></use></svg><svg aria-hidden="true" class="hidden h-5 w-5 group-open:block"><use href="/build/_assets/icons-UIZOAPCC.svg#chevron-d"></use></svg></div><div class="whitespace-nowrap font-bold">Tutorial</div></summary><div class="absolute h-[66vh] w-full overflow-auto overscroll-contain border-b bg-white p-3 shadow-2xl dark:border-gray-700 dark:bg-gray-900 dark:shadow-black"><nav><ul><li class="mb-6"><div class="mb-2 block font-bold lg:text-sm">Getting Started</div><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/start/overview">Feature Overview<!-- --> </a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm bg-gray-50 font-semibold text-red-brand dark:bg-gray-800" href="https://reactrouter.com/en/main/start/tutorial">Tutorial<!-- --> </a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/start/examples">Examples<!-- --> </a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/start/faq">FAQs<!-- --> </a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/start/concepts">Main Concepts<!-- --> </a></li><li class="mb-6"><div class="mb-2 block font-bold lg:text-sm">Upgrading</div><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/upgrading/v5">Upgrading from v5<!-- --> </a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/upgrading/reach">Migrating from @reach/router<!-- --> </a></li><li class="mb-6"><div class="mb-2 block font-bold lg:text-sm">Routers</div><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/routers/picking-a-router">Picking a Router<!-- --> <!-- -->🆕</a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/routers/create-browser-router">createBrowserRouter<!-- --> <!-- -->🆕</a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/routers/create-hash-router">createHashRouter<!-- --> <!-- -->🆕</a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/routers/create-memory-router">createMemoryRouter<!-- --> <!-- -->🆕</a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/routers/router-provider">RouterProvider<!-- --> <!-- -->🆕</a></li><li class="mb-6"><div class="mb-2 block font-bold lg:text-sm">Router Components</div><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/router-components/browser-router">BrowserRouter<!-- --> </a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/router-components/hash-router">HashRouter<!-- --> </a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/router-components/memory-router">MemoryRouter<!-- --> </a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/router-components/native-router">NativeRouter<!-- --> </a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/router-components/router">Router<!-- --> </a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/router-components/static-router">StaticRouter<!-- --> </a></li><li class="mb-6"><div class="mb-2 block font-bold lg:text-sm">Route</div><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/route/route">Route<!-- --> <!-- -->🆕</a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/route/action">action<!-- --> <!-- -->🆕</a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/route/error-element">errorElement<!-- --> <!-- -->🆕</a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/route/loader">loader<!-- --> <!-- -->🆕</a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/route/should-revalidate">shouldRevalidate<!-- --> <!-- -->🆕</a></li><li class="mb-6"><div class="mb-2 block font-bold lg:text-sm">Components</div><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/components/await">Await<!-- --> <!-- -->🆕</a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/components/form">Form<!-- --> <!-- -->🆕</a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/components/link">Link<!-- --> </a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/components/link-native">Link (RN)<!-- --> </a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/components/nav-link">NavLink<!-- --> </a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/components/navigate">Navigate<!-- --> </a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/components/outlet">Outlet<!-- --> </a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/components/route">Route<!-- --> </a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/components/routes">Routes<!-- --> </a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/components/scroll-restoration">ScrollRestoration<!-- --> <!-- -->🆕</a></li><li class="mb-6"><div class="mb-2 block font-bold lg:text-sm">Hooks</div><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/hooks/use-action-data">useActionData<!-- --> <!-- -->🆕</a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/hooks/use-async-error">useAsyncError<!-- --> <!-- -->🆕</a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/hooks/use-async-value">useAsyncValue<!-- --> <!-- -->🆕</a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/hooks/use-fetcher">useFetcher<!-- --> <!-- -->🆕</a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/hooks/use-fetchers">useFetchers<!-- --> <!-- -->🆕</a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/hooks/use-form-action">useFormAction<!-- --> <!-- -->🆕</a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/hooks/use-href">useHref<!-- --> </a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/hooks/use-in-router-context">useInRouterContext<!-- --> </a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/hooks/use-link-click-handler">useLinkClickHandler<!-- --> </a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/hooks/use-link-press-handler">useLinkPressHandler<!-- --> </a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/hooks/use-loader-data">useLoaderData<!-- --> <!-- -->🆕</a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/hooks/use-location">useLocation<!-- --> </a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/hooks/use-match">useMatch<!-- --> </a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/hooks/use-matches">useMatches<!-- --> <!-- -->🆕</a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/hooks/use-navigate">useNavigate<!-- --> </a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/hooks/use-navigation">useNavigation<!-- --> <!-- -->🆕</a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/hooks/use-navigation-type">useNavigationType<!-- --> </a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/hooks/use-outlet">useOutlet<!-- --> </a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/hooks/use-outlet-context">useOutletContext<!-- --> </a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/hooks/use-params">useParams<!-- --> </a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/hooks/use-resolved-path">useResolvedPath<!-- --> </a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/hooks/use-revalidator">useRevalidator<!-- --> <!-- -->🆕</a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/hooks/use-route-error">useRouteError<!-- --> <!-- -->🆕</a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/hooks/use-route-loader-data">useRouteLoaderData<!-- --> <!-- -->🆕</a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/hooks/use-routes">useRoutes<!-- --> </a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/hooks/use-search-params">useSearchParams<!-- --> </a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/hooks/use-search-params-rn">useSearchParams (RN)<!-- --> </a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/hooks/use-submit">useSubmit<!-- --> <!-- -->🆕</a></li><li class="mb-6"><div class="mb-2 block font-bold lg:text-sm">Fetch Utilities</div><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/fetch/json">json<!-- --> <!-- -->🆕</a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/fetch/redirect">redirect<!-- --> <!-- -->🆕</a></li><li class="mb-6"><div class="mb-2 block font-bold lg:text-sm">Utilities</div><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/utils/create-routes-from-children">createRoutesFromChildren<!-- --> </a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/utils/create-routes-from-elements">createRoutesFromElements<!-- --> </a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/utils/create-search-params">createSearchParams<!-- --> </a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/utils/defer">defer<!-- --> <!-- -->🆕</a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/utils/generate-path">generatePath<!-- --> </a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/utils/is-route-error-response">isRouteErrorResponse<!-- --> <!-- -->🆕</a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/utils/location">Location<!-- --> </a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/utils/match-path">matchPath<!-- --> </a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/utils/match-routes">matchRoutes<!-- --> </a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/utils/render-matches">renderMatches<!-- --> </a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/utils/resolve-path">resolvePath<!-- --> </a></li><li class="mb-6"><div class="mb-2 block font-bold lg:text-sm">Guides</div><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/guides/ssr">Server-Side Rendering<!-- --> </a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/guides/contributing">Contributing<!-- --> </a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/guides/data-libs">Data Library Integration<!-- --> </a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/guides/deferred">Deferred Data<!-- --> <!-- -->🆕</a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/guides/form-data">Working With FormData<!-- --> <!-- -->🆕</a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/guides/index-search-param">Index Query Param<!-- --> <!-- -->🆕</a></li></ul></nav></div></details></div><div class=""><div class="fixed top-16 bottom-0 hidden w-72 overflow-auto py-6 pl-8 pr-6 lg:block"><nav><ul><li class="mb-6"><div class="mb-2 block font-bold lg:text-sm">Getting Started</div><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/start/overview">Feature Overview<!-- --> </a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm bg-gray-50 font-semibold text-red-brand dark:bg-gray-800" href="https://reactrouter.com/en/main/start/tutorial">Tutorial<!-- --> </a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/start/examples">Examples<!-- --> </a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/start/faq">FAQs<!-- --> </a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/start/concepts">Main Concepts<!-- --> </a></li><li class="mb-6"><div class="mb-2 block font-bold lg:text-sm">Upgrading</div><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/upgrading/v5">Upgrading from v5<!-- --> </a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/upgrading/reach">Migrating from @reach/router<!-- --> </a></li><li class="mb-6"><div class="mb-2 block font-bold lg:text-sm">Routers</div><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/routers/picking-a-router">Picking a Router<!-- --> <!-- -->🆕</a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/routers/create-browser-router">createBrowserRouter<!-- --> <!-- -->🆕</a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/routers/create-hash-router">createHashRouter<!-- --> <!-- -->🆕</a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/routers/create-memory-router">createMemoryRouter<!-- --> <!-- -->🆕</a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/routers/router-provider">RouterProvider<!-- --> <!-- -->🆕</a></li><li class="mb-6"><div class="mb-2 block font-bold lg:text-sm">Router Components</div><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/router-components/browser-router">BrowserRouter<!-- --> </a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/router-components/hash-router">HashRouter<!-- --> </a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/router-components/memory-router">MemoryRouter<!-- --> </a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/router-components/native-router">NativeRouter<!-- --> </a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/router-components/router">Router<!-- --> </a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/router-components/static-router">StaticRouter<!-- --> </a></li><li class="mb-6"><div class="mb-2 block font-bold lg:text-sm">Route</div><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/route/route">Route<!-- --> <!-- -->🆕</a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/route/action">action<!-- --> <!-- -->🆕</a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/route/error-element">errorElement<!-- --> <!-- -->🆕</a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/route/loader">loader<!-- --> <!-- -->🆕</a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/route/should-revalidate">shouldRevalidate<!-- --> <!-- -->🆕</a></li><li class="mb-6"><div class="mb-2 block font-bold lg:text-sm">Components</div><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/components/await">Await<!-- --> <!-- -->🆕</a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/components/form">Form<!-- --> <!-- -->🆕</a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/components/link">Link<!-- --> </a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/components/link-native">Link (RN)<!-- --> </a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/components/nav-link">NavLink<!-- --> </a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/components/navigate">Navigate<!-- --> </a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/components/outlet">Outlet<!-- --> </a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/components/route">Route<!-- --> </a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/components/routes">Routes<!-- --> </a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/components/scroll-restoration">ScrollRestoration<!-- --> <!-- -->🆕</a></li><li class="mb-6"><div class="mb-2 block font-bold lg:text-sm">Hooks</div><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/hooks/use-action-data">useActionData<!-- --> <!-- -->🆕</a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/hooks/use-async-error">useAsyncError<!-- --> <!-- -->🆕</a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/hooks/use-async-value">useAsyncValue<!-- --> <!-- -->🆕</a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/hooks/use-fetcher">useFetcher<!-- --> <!-- -->🆕</a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/hooks/use-fetchers">useFetchers<!-- --> <!-- -->🆕</a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/hooks/use-form-action">useFormAction<!-- --> <!-- -->🆕</a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/hooks/use-href">useHref<!-- --> </a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/hooks/use-in-router-context">useInRouterContext<!-- --> </a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/hooks/use-link-click-handler">useLinkClickHandler<!-- --> </a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/hooks/use-link-press-handler">useLinkPressHandler<!-- --> </a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/hooks/use-loader-data">useLoaderData<!-- --> <!-- -->🆕</a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/hooks/use-location">useLocation<!-- --> </a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/hooks/use-match">useMatch<!-- --> </a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/hooks/use-matches">useMatches<!-- --> <!-- -->🆕</a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/hooks/use-navigate">useNavigate<!-- --> </a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/hooks/use-navigation">useNavigation<!-- --> <!-- -->🆕</a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/hooks/use-navigation-type">useNavigationType<!-- --> </a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/hooks/use-outlet">useOutlet<!-- --> </a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/hooks/use-outlet-context">useOutletContext<!-- --> </a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/hooks/use-params">useParams<!-- --> </a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/hooks/use-resolved-path">useResolvedPath<!-- --> </a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/hooks/use-revalidator">useRevalidator<!-- --> <!-- -->🆕</a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/hooks/use-route-error">useRouteError<!-- --> <!-- -->🆕</a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/hooks/use-route-loader-data">useRouteLoaderData<!-- --> <!-- -->🆕</a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/hooks/use-routes">useRoutes<!-- --> </a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/hooks/use-search-params">useSearchParams<!-- --> </a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/hooks/use-search-params-rn">useSearchParams (RN)<!-- --> </a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/hooks/use-submit">useSubmit<!-- --> <!-- -->🆕</a></li><li class="mb-6"><div class="mb-2 block font-bold lg:text-sm">Fetch Utilities</div><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/fetch/json">json<!-- --> <!-- -->🆕</a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/fetch/redirect">redirect<!-- --> <!-- -->🆕</a></li><li class="mb-6"><div class="mb-2 block font-bold lg:text-sm">Utilities</div><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/utils/create-routes-from-children">createRoutesFromChildren<!-- --> </a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/utils/create-routes-from-elements">createRoutesFromElements<!-- --> </a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/utils/create-search-params">createSearchParams<!-- --> </a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/utils/defer">defer<!-- --> <!-- -->🆕</a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/utils/generate-path">generatePath<!-- --> </a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/utils/is-route-error-response">isRouteErrorResponse<!-- --> <!-- -->🆕</a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/utils/location">Location<!-- --> </a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/utils/match-path">matchPath<!-- --> </a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/utils/match-routes">matchRoutes<!-- --> </a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/utils/render-matches">renderMatches<!-- --> </a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/utils/resolve-path">resolvePath<!-- --> </a></li><li class="mb-6"><div class="mb-2 block font-bold lg:text-sm">Guides</div><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/guides/ssr">Server-Side Rendering<!-- --> </a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/guides/contributing">Contributing<!-- --> </a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/guides/data-libs">Data Library Integration<!-- --> </a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/guides/deferred">Deferred Data<!-- --> <!-- -->🆕</a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/guides/form-data">Working With FormData<!-- --> <!-- -->🆕</a><a class="group my-1 flex items-center rounded-md border-transparent py-1.5 pl-4 lg:text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand" href="https://reactrouter.com/en/main/guides/index-search-param">Index Query Param<!-- --> <!-- -->🆕</a></li></ul></nav></div><div class="min-h-[80vh]"><div class="xl:flex xl:w-full xl:gap-8"><details class="group flex h-full flex-col lg:ml-80 lg:mt-4 xl:hidden"><summary class="_no-triangle flex cursor-pointer select-none items-center gap-2 border-b border-gray-50 bg-white px-2 py-3 text-sm font-medium hover:bg-gray-50 active:bg-gray-100 dark:border-gray-700 dark:bg-gray-900 dark:hover:bg-gray-800 dark:active:bg-gray-700"><div class="flex items-center gap-2"><svg aria-hidden="true" class="h-5 w-5 group-open:hidden"><use href="/build/_assets/icons-UIZOAPCC.svg#chevron-r"></use></svg><svg aria-hidden="true" class="hidden h-5 w-5 group-open:block"><use href="/build/_assets/icons-UIZOAPCC.svg#chevron-d"></use></svg></div><div class="whitespace-nowrap">On this page</div></summary><ul class="pl-9"><li><a href="https://reactrouter.com/en/main/start/tutorial#setup" class="block py-2 text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand">Setup</a></li><li><a href="https://reactrouter.com/en/main/start/tutorial#adding-a-router" class="block py-2 text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand">Adding a Router</a></li><li><a href="https://reactrouter.com/en/main/start/tutorial#the-root-route" class="block py-2 text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand">The Root Route</a></li><li><a href="https://reactrouter.com/en/main/start/tutorial#handling-not-found-errors" class="block py-2 text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand">Handling Not Found Errors</a></li><li><a href="https://reactrouter.com/en/main/start/tutorial#the-contact-route-ui" class="block py-2 text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand">The Contact Route UI</a></li><li><a href="https://reactrouter.com/en/main/start/tutorial#nested-routes" class="block py-2 text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand">Nested Routes</a></li><li><a href="https://reactrouter.com/en/main/start/tutorial#client-side-routing" class="block py-2 text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand">Client Side Routing</a></li><li><a href="https://reactrouter.com/en/main/start/tutorial#loading-data" class="block py-2 text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand">Loading Data</a></li><li><a href="https://reactrouter.com/en/main/start/tutorial#data-writes--html-forms" class="block py-2 text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand">Data Writes + HTML Forms</a></li><li><a href="https://reactrouter.com/en/main/start/tutorial#creating-contacts" class="block py-2 text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand">Creating Contacts</a></li><li><a href="https://reactrouter.com/en/main/start/tutorial#url-params-in-loaders" class="block py-2 text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand">URL Params in Loaders</a></li><li><a href="https://reactrouter.com/en/main/start/tutorial#updating-data" class="block py-2 text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand">Updating Data</a></li><li><a href="https://reactrouter.com/en/main/start/tutorial#updating-contacts-with-formdata" class="block py-2 text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand">Updating Contacts with FormData</a></li><li><a href="https://reactrouter.com/en/main/start/tutorial#mutation-discussion" class="block py-2 text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand">Mutation Discussion</a></li><li><a href="https://reactrouter.com/en/main/start/tutorial#redirecting-new-records-to-the-edit-page" class="block py-2 text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand">Redirecting new records to the edit page</a></li><li><a href="https://reactrouter.com/en/main/start/tutorial#active-link-styling" class="block py-2 text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand">Active Link Styling</a></li><li><a href="https://reactrouter.com/en/main/start/tutorial#global-pending-ui" class="block py-2 text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand">Global Pending UI</a></li><li><a href="https://reactrouter.com/en/main/start/tutorial#deleting-records" class="block py-2 text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand">Deleting Records</a></li><li><a href="https://reactrouter.com/en/main/start/tutorial#contextual-errors" class="block py-2 text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand">Contextual Errors</a></li><li><a href="https://reactrouter.com/en/main/start/tutorial#index-routes" class="block py-2 text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand">Index Routes</a></li><li><a href="https://reactrouter.com/en/main/start/tutorial#cancel-button" class="block py-2 text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand">Cancel Button</a></li><li><a href="https://reactrouter.com/en/main/start/tutorial#url-search-params-and-get-submissions" class="block py-2 text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand">URL Search Params and GET Submissions</a></li><li><a href="https://reactrouter.com/en/main/start/tutorial#get-submissions-with-client-side-routing" class="block py-2 text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand">GET Submissions with Client Side Routing</a></li><li><a href="https://reactrouter.com/en/main/start/tutorial#synchronizing-urls-to-form-state" class="block py-2 text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand">Synchronizing URLs to Form State</a></li><li><a href="https://reactrouter.com/en/main/start/tutorial#submitting-forms-onchange" class="block py-2 text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand">Submitting Forms <code>onChange</code></a></li><li><a href="https://reactrouter.com/en/main/start/tutorial#adding-search-spinner" class="block py-2 text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand">Adding Search Spinner</a></li><li><a href="https://reactrouter.com/en/main/start/tutorial#managing-the-history-stack" class="block py-2 text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand">Managing the History Stack</a></li><li><a href="https://reactrouter.com/en/main/start/tutorial#mutations-without-navigation" class="block py-2 text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand">Mutations Without Navigation</a></li><li><a href="https://reactrouter.com/en/main/start/tutorial#optimistic-ui" class="block py-2 text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand">Optimistic UI</a></li><li><a href="https://reactrouter.com/en/main/start/tutorial#not-found-data" class="block py-2 text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand">Not Found Data</a></li><li><a href="https://reactrouter.com/en/main/start/tutorial#pathless-routes" class="block py-2 text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand">Pathless Routes</a></li><li><a href="https://reactrouter.com/en/main/start/tutorial#jsx-routes" class="block py-2 text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand">JSX Routes</a></li></ul></details><div class="hidden xl:sticky xl:top-28 xl:order-1 xl:mt-10 xl:block xl:max-h-[calc(100vh-10rem)] xl:w-56 xl:flex-shrink-0 xl:self-start xl:overflow-auto"><nav class="mb-2 text-sm font-bold">On this page</nav><ul><li><a href="https://reactrouter.com/en/main/start/tutorial#setup" class="block py-1 text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand">Setup</a></li><li><a href="https://reactrouter.com/en/main/start/tutorial#adding-a-router" class="block py-1 text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand">Adding a Router</a></li><li><a href="https://reactrouter.com/en/main/start/tutorial#the-root-route" class="block py-1 text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand">The Root Route</a></li><li><a href="https://reactrouter.com/en/main/start/tutorial#handling-not-found-errors" class="block py-1 text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand">Handling Not Found Errors</a></li><li><a href="https://reactrouter.com/en/main/start/tutorial#the-contact-route-ui" class="block py-1 text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand">The Contact Route UI</a></li><li><a href="https://reactrouter.com/en/main/start/tutorial#nested-routes" class="block py-1 text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand">Nested Routes</a></li><li><a href="https://reactrouter.com/en/main/start/tutorial#client-side-routing" class="block py-1 text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand">Client Side Routing</a></li><li><a href="https://reactrouter.com/en/main/start/tutorial#loading-data" class="block py-1 text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand">Loading Data</a></li><li><a href="https://reactrouter.com/en/main/start/tutorial#data-writes--html-forms" class="block py-1 text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand">Data Writes + HTML Forms</a></li><li><a href="https://reactrouter.com/en/main/start/tutorial#creating-contacts" class="block py-1 text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand">Creating Contacts</a></li><li><a href="https://reactrouter.com/en/main/start/tutorial#url-params-in-loaders" class="block py-1 text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand">URL Params in Loaders</a></li><li><a href="https://reactrouter.com/en/main/start/tutorial#updating-data" class="block py-1 text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand">Updating Data</a></li><li><a href="https://reactrouter.com/en/main/start/tutorial#updating-contacts-with-formdata" class="block py-1 text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand">Updating Contacts with FormData</a></li><li><a href="https://reactrouter.com/en/main/start/tutorial#mutation-discussion" class="block py-1 text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand">Mutation Discussion</a></li><li><a href="https://reactrouter.com/en/main/start/tutorial#redirecting-new-records-to-the-edit-page" class="block py-1 text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand">Redirecting new records to the edit page</a></li><li><a href="https://reactrouter.com/en/main/start/tutorial#active-link-styling" class="block py-1 text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand">Active Link Styling</a></li><li><a href="https://reactrouter.com/en/main/start/tutorial#global-pending-ui" class="block py-1 text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand">Global Pending UI</a></li><li><a href="https://reactrouter.com/en/main/start/tutorial#deleting-records" class="block py-1 text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand">Deleting Records</a></li><li><a href="https://reactrouter.com/en/main/start/tutorial#contextual-errors" class="block py-1 text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand">Contextual Errors</a></li><li><a href="https://reactrouter.com/en/main/start/tutorial#index-routes" class="block py-1 text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand">Index Routes</a></li><li><a href="https://reactrouter.com/en/main/start/tutorial#cancel-button" class="block py-1 text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand">Cancel Button</a></li><li><a href="https://reactrouter.com/en/main/start/tutorial#url-search-params-and-get-submissions" class="block py-1 text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand">URL Search Params and GET Submissions</a></li><li><a href="https://reactrouter.com/en/main/start/tutorial#get-submissions-with-client-side-routing" class="block py-1 text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand">GET Submissions with Client Side Routing</a></li><li><a href="https://reactrouter.com/en/main/start/tutorial#synchronizing-urls-to-form-state" class="block py-1 text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand">Synchronizing URLs to Form State</a></li><li><a href="https://reactrouter.com/en/main/start/tutorial#submitting-forms-onchange" class="block py-1 text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand">Submitting Forms <code>onChange</code></a></li><li><a href="https://reactrouter.com/en/main/start/tutorial#adding-search-spinner" class="block py-1 text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand">Adding Search Spinner</a></li><li><a href="https://reactrouter.com/en/main/start/tutorial#managing-the-history-stack" class="block py-1 text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand">Managing the History Stack</a></li><li><a href="https://reactrouter.com/en/main/start/tutorial#mutations-without-navigation" class="block py-1 text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand">Mutations Without Navigation</a></li><li><a href="https://reactrouter.com/en/main/start/tutorial#optimistic-ui" class="block py-1 text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand">Optimistic UI</a></li><li><a href="https://reactrouter.com/en/main/start/tutorial#not-found-data" class="block py-1 text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand">Not Found Data</a></li><li><a href="https://reactrouter.com/en/main/start/tutorial#pathless-routes" class="block py-1 text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand">Pathless Routes</a></li><li><a href="https://reactrouter.com/en/main/start/tutorial#jsx-routes" class="block py-1 text-sm text-gray-400 hover:text-gray-900 active:text-red-brand dark:text-gray-400 dark:hover:text-gray-50 dark:active:text-red-brand">JSX Routes</a></li></ul></div><div class="px-4 pt-8 pb-4 lg:ml-72 lg:pr-8 lg:pl-12 xl:flex-grow"><div class="markdown w-full pb-[33vh]"><div class="md-prose"><h1 id="tutorial"><a href="https://reactrouter.com/en/main/start/tutorial#tutorial" aria-hidden="true" tabindex="-1"><span class="icon icon-link"></span></a>Tutorial</h1><p>Welcome to the tutorial! We'll be building a small, but feature-rich app that lets you keep track of your contacts. We expect it to take between 30-60m if you're following along.</p><img class="tutorial" src="./Tutorial v6.4.3 _ React Router_files/15.webp"><p>👉 <strong>Every time you see this it means you need to do something in the app!</strong></p><p>The rest is just there for your information and deeper understanding. Let's get to it.</p><h2 id="setup"><a href="https://reactrouter.com/en/main/start/tutorial#setup" aria-hidden="true" tabindex="-1"><span class="icon icon-link"></span></a>Setup</h2><p><docs-info>If you're not going to follow along in your own app, you can skip this section</docs-info></p><p>We'll be using <a href="https://vitejs.dev/guide/">Vite</a> for our bundler and dev server for this tutorial. You'll need <a href="https://nodejs.org/">Node.js</a> installed for the <code>npm</code> command line tool.</p><p>👉️ <strong>Open up your terminal and bootstrap a new React app with Vite:</strong></p><pre><code class="language-sh">npm create vite@latest name-of-your-project -- --template react
# follow prompts
cd <your new project directory>
npm install react-router-dom localforage match-sorter sort-by
npm run dev
</code></pre><p>You should be able to visit the URL printed in the terminal:</p><pre><code> VITE v3.0.7 ready in 175 ms
➜ Local: http://127.0.0.1:5173/
➜ Network: use --host to expose
</code></pre><p>We've got some pre-written CSS for this tutorial so we can stay focused on React Router. Feel free to judge it harshly or write your own 😅 (We did things we normally wouldn't in CSS so that the markup in this tutorial could stay as minimal as possible.)</p><p>👉 <strong>Copy/Paste the tutorial CSS <a href="https://gist.githubusercontent.com/ryanflorence/ba20d473ef59e1965543fa013ae4163f/raw/499707f25a5690d490c7b3d54c65c65eb895930c/react-router-6.4-tutorial-css.css">found here</a> into <code>src/index.css</code></strong></p><p>This tutorial will be creating, reading, searching, updating, and deleting data. A typical web app would probably be talking to an API on your web server, but we're going to use browser storage and fake some network latency to keep this focused. None of this code is relevant to React Router, so just go ahead and copy/paste it all.</p><p>👉 <strong>Copy/Paste the tutorial data module <a href="https://gist.githubusercontent.com/ryanflorence/1e7f5d3344c0db4a8394292c157cd305/raw/f7ff21e9ae7ffd55bfaaaf320e09c6a08a8a6611/contacts.js">found here</a> into <code>src/contacts.js</code></strong></p><p>All you need in the src folder are <code>contacts.js</code>, <code>main.jsx</code>, and <code>index.css</code>. You can delete anything else (like <code>App.js</code> and <code>assets</code>, etc.).</p><p>👉 <strong>Delete unused files in <code>src/</code> so all you have left are these:</strong></p><pre><code>src
├── contacts.js
├── index.css
└── main.jsx
</code></pre><p>If your app is running, it might blow up momentarily, just keep going 😋. And with that, we're ready to get started!</p><h2 id="adding-a-router"><a href="https://reactrouter.com/en/main/start/tutorial#adding-a-router" aria-hidden="true" tabindex="-1"><span class="icon icon-link"></span></a>Adding a Router</h2><p>First thing to do is create a <a href="https://reactrouter.com/en/main/routers/create-browser-router">Browser Router</a> and configure our first route. This will enable client side routing for our web app.</p><p>The <code>main.jsx</code> file is the entry point. Open it up and we'll put React Router on the page.</p><p>👉 <strong>Create and render a <a href="https://reactrouter.com/en/main/routers/create-browser-router">browser router</a> in <code>main.jsx</code></strong></p><div><pre data-filename="src/main.jsx" data-line-numbers="true" data-lang="jsx" style="color: var(--base05);background-color: var(--base00)"><code><span class="codeblock-line" data-line-number="1"><span style="color: var(--base0E)">import</span> <span style="color: var(--base08)">React</span> <span style="color: var(--base0E)">from</span> "<span style="color: var(--base0B)">react</span>";
</span><span class="codeblock-line" data-line-number="2"><span style="color: var(--base0E)">import</span> <span style="color: var(--base08)">ReactDOM</span> <span style="color: var(--base0E)">from</span> "<span style="color: var(--base0B)">react-dom/client</span>";
</span><span class="codeblock-line" data-highlight="true" data-line-number="3"><span style="color: var(--base0E)">import</span> {
</span><span class="codeblock-line" data-highlight="true" data-line-number="4"> <span style="color: var(--base08)">createBrowserRouter</span>,
</span><span class="codeblock-line" data-highlight="true" data-line-number="5"> <span style="color: var(--base08)">RouterProvider</span>,
</span><span class="codeblock-line" data-highlight="true" data-line-number="6"> <span style="color: var(--base08)">Route</span>,
</span><span class="codeblock-line" data-highlight="true" data-line-number="7">} <span style="color: var(--base0E)">from</span> "<span style="color: var(--base0B)">react-router-dom</span>";
</span><span class="codeblock-line" data-line-number="8"><span style="color: var(--base0E)">import</span> "<span style="color: var(--base0B)">./index.css</span>";
</span><span class="codeblock-line" data-line-number="9">
</span><span class="codeblock-line" data-highlight="true" data-line-number="10"><span style="color: var(--base0A)">const</span> <span style="color: var(--base08)">router</span> <span style="color: var(--base0E)">=</span> <span style="color: var(--base0D)">createBrowserRouter</span>([
</span><span class="codeblock-line" data-highlight="true" data-line-number="11"> {
</span><span class="codeblock-line" data-highlight="true" data-line-number="12"> path: "<span style="color: var(--base0B)">/</span>",
</span><span class="codeblock-line" data-highlight="true" data-line-number="13"> element: <<span style="color: var(--base08)">div</span>>Hello world!</<span style="color: var(--base08)">div</span>>,
</span><span class="codeblock-line" data-highlight="true" data-line-number="14"> },
</span><span class="codeblock-line" data-highlight="true" data-line-number="15">]);
</span><span class="codeblock-line" data-line-number="16">
</span><span class="codeblock-line" data-line-number="17"><span style="color: var(--base08)">ReactDOM</span>.<span style="color: var(--base0D)">createRoot</span>(<span style="color: var(--base08)">document</span>.<span style="color: var(--base0D)">getElementById</span>("<span style="color: var(--base0B)">root</span>")).<span style="color: var(--base0D)">render</span>(
</span><span class="codeblock-line" data-line-number="18"> <<span style="color: var(--base0A)">React.StrictMode</span>>
</span><span class="codeblock-line" data-highlight="true" data-line-number="19"> <<span style="color: var(--base0A)">RouterProvider</span> <span style="color: var(--base0D)">router</span><span style="color: var(--base0E)">=</span><span style="color: var(--base0F)">{</span><span style="color: var(--base08)">router</span><span style="color: var(--base0F)">}</span> />
</span><span class="codeblock-line" data-line-number="20"> </<span style="color: var(--base0A)">React.StrictMode</span>>
</span><span class="codeblock-line" data-line-number="21">);
</span></code></pre></div><p>This first route is what we often call the "root route" since the rest of our routes will render inside of it. It will serve as the root layout of the UI, we'll have nested layouts as we get farther along.</p><h2 id="the-root-route"><a href="https://reactrouter.com/en/main/start/tutorial#the-root-route" aria-hidden="true" tabindex="-1"><span class="icon icon-link"></span></a>The Root Route</h2><p>Let's add the global layout for this app.</p><p>👉 <strong>Create <code>src/routes</code> and <code>src/routes/root.jsx</code></strong></p><pre><code class="language-sh">mkdir src/routes
touch src/routes/root.jsx
</code></pre><p><small>(If you don't want to be a command line nerd, use your editor instead of those commands 🤓)</small></p><p>👉 <strong>Create the root layout component</strong></p><div><pre data-filename="src/routes/root.jsx" data-line-numbers="true" data-lang="jsx" style="color: var(--base05);background-color: var(--base00)"><code><span class="codeblock-line" data-line-number="1"><span style="color: var(--base0E)">export</span> <span style="color: var(--base0E)">default</span> <span style="color: var(--base0D)">function</span> <span style="color: var(--base0D)">Root</span>() {
</span><span class="codeblock-line" data-line-number="2"> <span style="color: var(--base0E)">return</span> (
</span><span class="codeblock-line" data-line-number="3"> <>
</span><span class="codeblock-line" data-line-number="4"> <<span style="color: var(--base08)">div</span> <span style="color: var(--base0D)">id</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">sidebar</span>">
</span><span class="codeblock-line" data-line-number="5"> <<span style="color: var(--base08)">h1</span>>React Router Contacts</<span style="color: var(--base08)">h1</span>>
</span><span class="codeblock-line" data-line-number="6"> <<span style="color: var(--base08)">div</span>>
</span><span class="codeblock-line" data-line-number="7"> <<span style="color: var(--base08)">form</span> <span style="color: var(--base0D)">id</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">search-form</span>" <span style="color: var(--base0D)">role</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">search</span>">
</span><span class="codeblock-line" data-line-number="8"> <<span style="color: var(--base08)">input</span>
</span><span class="codeblock-line" data-line-number="9"> <span style="color: var(--base0D)">id</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">q</span>"
</span><span class="codeblock-line" data-line-number="10"> <span style="color: var(--base0D)">aria-label</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">Search contacts</span>"
</span><span class="codeblock-line" data-line-number="11"> <span style="color: var(--base0D)">placeholder</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">Search</span>"
</span><span class="codeblock-line" data-line-number="12"> <span style="color: var(--base0D)">type</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">search</span>"
</span><span class="codeblock-line" data-line-number="13"> <span style="color: var(--base0D)">name</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">q</span>"
</span><span class="codeblock-line" data-line-number="14"> />
</span><span class="codeblock-line" data-line-number="15"> <<span style="color: var(--base08)">div</span>
</span><span class="codeblock-line" data-line-number="16"> <span style="color: var(--base0D)">id</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">search-spinner</span>"
</span><span class="codeblock-line" data-line-number="17"> <span style="color: var(--base0D)">aria-hidden</span>
</span><span class="codeblock-line" data-line-number="18"> <span style="color: var(--base0D)">hidden</span><span style="color: var(--base0E)">=</span><span style="color: var(--base0F)">{</span><span style="color: var(--base09)">true</span><span style="color: var(--base0F)">}</span>
</span><span class="codeblock-line" data-line-number="19"> />
</span><span class="codeblock-line" data-line-number="20"> <<span style="color: var(--base08)">div</span>
</span><span class="codeblock-line" data-line-number="21"> <span style="color: var(--base0D)">className</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">sr-only</span>"
</span><span class="codeblock-line" data-line-number="22"> <span style="color: var(--base0D)">aria-live</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">polite</span>"
</span><span class="codeblock-line" data-line-number="23"> ></<span style="color: var(--base08)">div</span>>
</span><span class="codeblock-line" data-line-number="24"> </<span style="color: var(--base08)">form</span>>
</span><span class="codeblock-line" data-line-number="25"> <<span style="color: var(--base08)">form</span> <span style="color: var(--base0D)">method</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">post</span>">
</span><span class="codeblock-line" data-line-number="26"> <<span style="color: var(--base08)">button</span> <span style="color: var(--base0D)">type</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">submit</span>">New</<span style="color: var(--base08)">button</span>>
</span><span class="codeblock-line" data-line-number="27"> </<span style="color: var(--base08)">form</span>>
</span><span class="codeblock-line" data-line-number="28"> </<span style="color: var(--base08)">div</span>>
</span><span class="codeblock-line" data-line-number="29"> <<span style="color: var(--base08)">nav</span>>
</span><span class="codeblock-line" data-line-number="30"> <<span style="color: var(--base08)">ul</span>>
</span><span class="codeblock-line" data-line-number="31"> <<span style="color: var(--base08)">li</span>>
</span><span class="codeblock-line" data-line-number="32"> <<span style="color: var(--base08)">a</span> <span style="color: var(--base0D)">href</span><span style="color: var(--base0E)">=</span><span style="color: var(--base0F)">{</span>`<span style="color: var(--base0B)">contacts/1</span>`<span style="color: var(--base0F)">}</span>>Your Name</<span style="color: var(--base08)">a</span>>
</span><span class="codeblock-line" data-line-number="33"> </<span style="color: var(--base08)">li</span>>
</span><span class="codeblock-line" data-line-number="34"> <<span style="color: var(--base08)">li</span>>
</span><span class="codeblock-line" data-line-number="35"> <<span style="color: var(--base08)">a</span> <span style="color: var(--base0D)">href</span><span style="color: var(--base0E)">=</span><span style="color: var(--base0F)">{</span>`<span style="color: var(--base0B)">contacts/2</span>`<span style="color: var(--base0F)">}</span>>Your Friend</<span style="color: var(--base08)">a</span>>
</span><span class="codeblock-line" data-line-number="36"> </<span style="color: var(--base08)">li</span>>
</span><span class="codeblock-line" data-line-number="37"> </<span style="color: var(--base08)">ul</span>>
</span><span class="codeblock-line" data-line-number="38"> </<span style="color: var(--base08)">nav</span>>
</span><span class="codeblock-line" data-line-number="39"> </<span style="color: var(--base08)">div</span>>
</span><span class="codeblock-line" data-line-number="40"> <<span style="color: var(--base08)">div</span> <span style="color: var(--base0D)">id</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">detail</span>"></<span style="color: var(--base08)">div</span>>
</span><span class="codeblock-line" data-line-number="41"> </>
</span><span class="codeblock-line" data-line-number="42"> );
</span><span class="codeblock-line" data-line-number="43">}
</span></code></pre></div><p>Nothing React Router specific yet, so feel free to copy/paste all of that.</p><p>👉 <strong>Set <code><Root></code> as the root route's <a href="https://reactrouter.com/en/main/route/route#element"><code>element</code></a></strong></p><div><pre data-filename="src/main.jsx" data-line-numbers="true" data-lang="jsx" style="color: var(--base05);background-color: var(--base00)"><code><span class="codeblock-line" data-line-number="1"><span style="color: var(--base03)">/* existing imports */</span>
</span><span class="codeblock-line" data-highlight="true" data-line-number="2"><span style="color: var(--base0E)">import</span> <span style="color: var(--base08)">Root</span> <span style="color: var(--base0E)">from</span> "<span style="color: var(--base0B)">./routes/root</span>";
</span><span class="codeblock-line" data-line-number="3">
</span><span class="codeblock-line" data-line-number="4"><span style="color: var(--base0A)">const</span> <span style="color: var(--base08)">router</span> <span style="color: var(--base0E)">=</span> <span style="color: var(--base0D)">createBrowserRouter</span>([
</span><span class="codeblock-line" data-line-number="5"> {
</span><span class="codeblock-line" data-line-number="6"> path: "<span style="color: var(--base0B)">/</span>",
</span><span class="codeblock-line" data-highlight="true" data-line-number="7"> element: <<span style="color: var(--base0A)">Root</span> />,
</span><span class="codeblock-line" data-line-number="8"> },
</span><span class="codeblock-line" data-line-number="9">]);
</span><span class="codeblock-line" data-line-number="10">
</span><span class="codeblock-line" data-line-number="11"><span style="color: var(--base08)">ReactDOM</span>.<span style="color: var(--base0D)">createRoot</span>(<span style="color: var(--base08)">document</span>.<span style="color: var(--base0D)">getElementById</span>("<span style="color: var(--base0B)">root</span>")).<span style="color: var(--base0D)">render</span>(
</span><span class="codeblock-line" data-line-number="12"> <<span style="color: var(--base0A)">React.StrictMode</span>>
</span><span class="codeblock-line" data-line-number="13"> <<span style="color: var(--base0A)">RouterProvider</span> <span style="color: var(--base0D)">router</span><span style="color: var(--base0E)">=</span><span style="color: var(--base0F)">{</span><span style="color: var(--base08)">router</span><span style="color: var(--base0F)">}</span> />
</span><span class="codeblock-line" data-line-number="14"> </<span style="color: var(--base0A)">React.StrictMode</span>>
</span><span class="codeblock-line" data-line-number="15">);
</span></code></pre></div><p>The app should look something like this now. It sure is nice having a designer who can also write the CSS, isn't it? (Thank you <a href="https://blog.jim-nielsen.com/">Jim</a> 🙏).</p><img class="tutorial" loading="lazy" src="./Tutorial v6.4.3 _ React Router_files/01.webp"><h2 id="handling-not-found-errors"><a href="https://reactrouter.com/en/main/start/tutorial#handling-not-found-errors" aria-hidden="true" tabindex="-1"><span class="icon icon-link"></span></a>Handling Not Found Errors</h2><p>It's always a good idea to know how your app responds to errors early in the project because we all write far more bugs than features when building a new app! Not only will your users get a good experience when this happens, but it helps you during development as well.</p><p>We added some links to this app, let's see what happens when we click them?</p><p>👉 <strong>Click one of the sidebar names</strong></p><img class="tutorial" loading="lazy" alt="screenshot of default React Router error element" src="./Tutorial v6.4.3 _ React Router_files/02.webp"><p>Gross! This is the default error screen in React Router, made worse by our flex box styles on the root element in this app 😂.</p><p>Anytime your app throws an error while rendering, loading data, or performing data mutations, React Router will catch it and render an error screen. Let's make our own error page.</p><p>👉 <strong>Create an error page component</strong></p><pre><code class="language-sh">touch src/error-page.jsx
</code></pre><div><pre data-filename="src/error-page.jsx" data-line-numbers="true" data-lang="jsx" style="color: var(--base05);background-color: var(--base00)"><code><span class="codeblock-line" data-line-number="1"><span style="color: var(--base0E)">import</span> { <span style="color: var(--base08)">useRouteError</span> } <span style="color: var(--base0E)">from</span> "<span style="color: var(--base0B)">react-router-dom</span>";
</span><span class="codeblock-line" data-line-number="2">
</span><span class="codeblock-line" data-line-number="3"><span style="color: var(--base0E)">export</span> <span style="color: var(--base0E)">default</span> <span style="color: var(--base0D)">function</span> <span style="color: var(--base0D)">ErrorPage</span>() {
</span><span class="codeblock-line" data-line-number="4"> <span style="color: var(--base0A)">const</span> <span style="color: var(--base08)">error</span> <span style="color: var(--base0E)">=</span> <span style="color: var(--base0D)">useRouteError</span>();
</span><span class="codeblock-line" data-line-number="5"> <span style="color: var(--base08)">console</span>.<span style="color: var(--base0D)">error</span>(<span style="color: var(--base08)">error</span>);
</span><span class="codeblock-line" data-line-number="6">
</span><span class="codeblock-line" data-line-number="7"> <span style="color: var(--base0E)">return</span> (
</span><span class="codeblock-line" data-line-number="8"> <<span style="color: var(--base08)">div</span> <span style="color: var(--base0D)">id</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">error-page</span>">
</span><span class="codeblock-line" data-line-number="9"> <<span style="color: var(--base08)">h1</span>>Oops!</<span style="color: var(--base08)">h1</span>>
</span><span class="codeblock-line" data-line-number="10"> <<span style="color: var(--base08)">p</span>>Sorry, an unexpected error has occurred.</<span style="color: var(--base08)">p</span>>
</span><span class="codeblock-line" data-line-number="11"> <<span style="color: var(--base08)">p</span>>
</span><span class="codeblock-line" data-line-number="12"> <<span style="color: var(--base08)">i</span>><span style="color: var(--base0F)">{</span><span style="color: var(--base08)">error</span>.<span style="color: var(--base08)">statusText</span> <span style="color: var(--base0E)">||</span> <span style="color: var(--base08)">error</span>.<span style="color: var(--base08)">message</span><span style="color: var(--base0F)">}</span></<span style="color: var(--base08)">i</span>>
</span><span class="codeblock-line" data-line-number="13"> </<span style="color: var(--base08)">p</span>>
</span><span class="codeblock-line" data-line-number="14"> </<span style="color: var(--base08)">div</span>>
</span><span class="codeblock-line" data-line-number="15"> );
</span><span class="codeblock-line" data-line-number="16">}
</span></code></pre></div><p>👉 <strong>Set the <code><ErrorPage></code> as the <a href="https://reactrouter.com/en/main/route/error-element"><code>errorElement</code></a> on the root route</strong></p><div><pre data-filename="src/main.jsx" data-line-numbers="true" data-lang="jsx" style="color: var(--base05);background-color: var(--base00)"><code><span class="codeblock-line" data-line-number="1"><span style="color: var(--base03)">/* previous imports */</span>
</span><span class="codeblock-line" data-highlight="true" data-line-number="2"><span style="color: var(--base0E)">import</span> <span style="color: var(--base08)">ErrorPage</span> <span style="color: var(--base0E)">from</span> "<span style="color: var(--base0B)">./error-page</span>";
</span><span class="codeblock-line" data-line-number="3">
</span><span class="codeblock-line" data-line-number="4"><span style="color: var(--base0A)">const</span> <span style="color: var(--base08)">router</span> <span style="color: var(--base0E)">=</span> <span style="color: var(--base0D)">createBrowserRouter</span>([
</span><span class="codeblock-line" data-line-number="5"> {
</span><span class="codeblock-line" data-line-number="6"> path: "<span style="color: var(--base0B)">/</span>",
</span><span class="codeblock-line" data-line-number="7"> element: <<span style="color: var(--base0A)">Root</span> />,
</span><span class="codeblock-line" data-highlight="true" data-line-number="8"> errorElement: <<span style="color: var(--base0A)">ErrorPage</span> />,
</span><span class="codeblock-line" data-line-number="9"> },
</span><span class="codeblock-line" data-line-number="10">]);
</span><span class="codeblock-line" data-line-number="11">
</span><span class="codeblock-line" data-line-number="12"><span style="color: var(--base08)">ReactDOM</span>.<span style="color: var(--base0D)">createRoot</span>(<span style="color: var(--base08)">document</span>.<span style="color: var(--base0D)">getElementById</span>("<span style="color: var(--base0B)">root</span>")).<span style="color: var(--base0D)">render</span>(
</span><span class="codeblock-line" data-line-number="13"> <<span style="color: var(--base0A)">React.StrictMode</span>>
</span><span class="codeblock-line" data-line-number="14"> <<span style="color: var(--base0A)">RouterProvider</span> <span style="color: var(--base0D)">router</span><span style="color: var(--base0E)">=</span><span style="color: var(--base0F)">{</span><span style="color: var(--base08)">router</span><span style="color: var(--base0F)">}</span> />
</span><span class="codeblock-line" data-line-number="15"> </<span style="color: var(--base0A)">React.StrictMode</span>>
</span><span class="codeblock-line" data-line-number="16">);
</span></code></pre></div><p>The error page should now look like this:</p><img class="tutorial" loading="lazy" alt="new error page, but still ugly" src="./Tutorial v6.4.3 _ React Router_files/03.webp"><p><small>(Well, that's not much better. Maybe somebody forgot to ask the designer to make an error page. Maybe everybody forgets to ask the designer to make an error page and then blames the designer for not thinking of it 😆)</small></p><p>Note that <a href="https://reactrouter.com/en/main/hooks/use-route-error"><code>useRouteError</code></a> provides the error that was thrown. When the user navigates to routes that don't exist you'll get an <a href="https://reactrouter.com/en/main/utils/is-route-error-response">error response</a> with a "Not Found" <code>statusText</code>. We'll see some other errors later in the tutorial and discuss them more.</p><p>For now, it's enough to know that pretty much all of your errors will now be handled by this page instead of infinite spinners, unresponsive pages, or blank screens 🙌</p><h2 id="the-contact-route-ui"><a href="https://reactrouter.com/en/main/start/tutorial#the-contact-route-ui" aria-hidden="true" tabindex="-1"><span class="icon icon-link"></span></a>The Contact Route UI</h2><p>Instead of a 404 "Not Found" page, we want to actually render something at the URLs we've linked to. For that, we need to make a new route.</p><p>👉 <strong>Create the contact route module</strong></p><pre><code class="language-sh">touch src/routes/contact.jsx
</code></pre><p>👉 <strong>Add the contact component UI</strong></p><p>It's just a bunch of elements, feel free to copy/paste.</p><div><pre data-filename="src/routes/contact.jsx" data-line-numbers="true" data-lang="jsx" style="color: var(--base05);background-color: var(--base00)"><code><span class="codeblock-line" data-line-number="1"><span style="color: var(--base0E)">import</span> { <span style="color: var(--base08)">Form</span> } <span style="color: var(--base0E)">from</span> "<span style="color: var(--base0B)">react-router-dom</span>";
</span><span class="codeblock-line" data-line-number="2">
</span><span class="codeblock-line" data-line-number="3"><span style="color: var(--base0E)">export</span> <span style="color: var(--base0E)">default</span> <span style="color: var(--base0D)">function</span> <span style="color: var(--base0D)">Contact</span>() {
</span><span class="codeblock-line" data-line-number="4"> <span style="color: var(--base0A)">const</span> <span style="color: var(--base08)">contact</span> <span style="color: var(--base0E)">=</span> {
</span><span class="codeblock-line" data-line-number="5"> first: "<span style="color: var(--base0B)">Your</span>",
</span><span class="codeblock-line" data-line-number="6"> last: "<span style="color: var(--base0B)">Name</span>",
</span><span class="codeblock-line" data-line-number="7"> avatar: "<span style="color: var(--base0B)">https://placekitten.com/g/200/200</span>",
</span><span class="codeblock-line" data-line-number="8"> twitter: "<span style="color: var(--base0B)">your_handle</span>",
</span><span class="codeblock-line" data-line-number="9"> notes: "<span style="color: var(--base0B)">Some notes</span>",
</span><span class="codeblock-line" data-line-number="10"> favorite: <span style="color: var(--base09)">true</span>,
</span><span class="codeblock-line" data-line-number="11"> };
</span><span class="codeblock-line" data-line-number="12">
</span><span class="codeblock-line" data-line-number="13"> <span style="color: var(--base0E)">return</span> (
</span><span class="codeblock-line" data-line-number="14"> <<span style="color: var(--base08)">div</span> <span style="color: var(--base0D)">id</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">contact</span>">
</span><span class="codeblock-line" data-line-number="15"> <<span style="color: var(--base08)">div</span>>
</span><span class="codeblock-line" data-line-number="16"> <<span style="color: var(--base08)">img</span>
</span><span class="codeblock-line" data-line-number="17"> <span style="color: var(--base0D)">key</span><span style="color: var(--base0E)">=</span><span style="color: var(--base0F)">{</span><span style="color: var(--base08)">contact</span>.<span style="color: var(--base08)">avatar</span><span style="color: var(--base0F)">}</span>
</span><span class="codeblock-line" data-line-number="18"> <span style="color: var(--base0D)">src</span><span style="color: var(--base0E)">=</span><span style="color: var(--base0F)">{</span><span style="color: var(--base08)">contact</span>.<span style="color: var(--base08)">avatar</span> <span style="color: var(--base0E)">||</span> <span style="color: var(--base09)">null</span><span style="color: var(--base0F)">}</span>
</span><span class="codeblock-line" data-line-number="19"> />
</span><span class="codeblock-line" data-line-number="20"> </<span style="color: var(--base08)">div</span>>
</span><span class="codeblock-line" data-line-number="21">
</span><span class="codeblock-line" data-line-number="22"> <<span style="color: var(--base08)">div</span>>
</span><span class="codeblock-line" data-line-number="23"> <<span style="color: var(--base08)">h1</span>>
</span><span class="codeblock-line" data-line-number="24"> <span style="color: var(--base0F)">{</span><span style="color: var(--base08)">contact</span>.<span style="color: var(--base08)">first</span> <span style="color: var(--base0E)">||</span> <span style="color: var(--base08)">contact</span>.<span style="color: var(--base08)">last</span> <span style="color: var(--base0E)">?</span> (
</span><span class="codeblock-line" data-line-number="25"> <>
</span><span class="codeblock-line" data-line-number="26"> <span style="color: var(--base0F)">{</span><span style="color: var(--base08)">contact</span>.<span style="color: var(--base08)">first</span><span style="color: var(--base0F)">}</span> <span style="color: var(--base0F)">{</span><span style="color: var(--base08)">contact</span>.<span style="color: var(--base08)">last</span><span style="color: var(--base0F)">}</span>
</span><span class="codeblock-line" data-line-number="27"> </>
</span><span class="codeblock-line" data-line-number="28"> ) <span style="color: var(--base0E)">:</span> (
</span><span class="codeblock-line" data-line-number="29"> <<span style="color: var(--base08)">i</span>>No Name</<span style="color: var(--base08)">i</span>>
</span><span class="codeblock-line" data-line-number="30"> )<span style="color: var(--base0F)">}{</span>"<span style="color: var(--base0B)"> </span>"<span style="color: var(--base0F)">}</span>
</span><span class="codeblock-line" data-line-number="31"> <<span style="color: var(--base0A)">Favorite</span> <span style="color: var(--base0D)">contact</span><span style="color: var(--base0E)">=</span><span style="color: var(--base0F)">{</span><span style="color: var(--base08)">contact</span><span style="color: var(--base0F)">}</span> />
</span><span class="codeblock-line" data-line-number="32"> </<span style="color: var(--base08)">h1</span>>
</span><span class="codeblock-line" data-line-number="33">
</span><span class="codeblock-line" data-line-number="34"> <span style="color: var(--base0F)">{</span><span style="color: var(--base08)">contact</span>.<span style="color: var(--base08)">twitter</span> <span style="color: var(--base0E)">&&</span> (
</span><span class="codeblock-line" data-line-number="35"> <<span style="color: var(--base08)">p</span>>
</span><span class="codeblock-line" data-line-number="36"> <<span style="color: var(--base08)">a</span>
</span><span class="codeblock-line" data-line-number="37"> <span style="color: var(--base0D)">target</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">_blank</span>"
</span><span class="codeblock-line" data-line-number="38"> <span style="color: var(--base0D)">href</span><span style="color: var(--base0E)">=</span><span style="color: var(--base0F)">{</span>`<span style="color: var(--base0B)">https://twitter.com/</span>${<span style="color: var(--base08)">contact</span>.<span style="color: var(--base08)">twitter</span>}`<span style="color: var(--base0F)">}</span>
</span><span class="codeblock-line" data-line-number="39"> >
</span><span class="codeblock-line" data-line-number="40"> <span style="color: var(--base0F)">{</span><span style="color: var(--base08)">contact</span>.<span style="color: var(--base08)">twitter</span><span style="color: var(--base0F)">}</span>
</span><span class="codeblock-line" data-line-number="41"> </<span style="color: var(--base08)">a</span>>
</span><span class="codeblock-line" data-line-number="42"> </<span style="color: var(--base08)">p</span>>
</span><span class="codeblock-line" data-line-number="43"> )<span style="color: var(--base0F)">}</span>
</span><span class="codeblock-line" data-line-number="44">
</span><span class="codeblock-line" data-line-number="45"> <span style="color: var(--base0F)">{</span><span style="color: var(--base08)">contact</span>.<span style="color: var(--base08)">notes</span> <span style="color: var(--base0E)">&&</span> <<span style="color: var(--base08)">p</span>><span style="color: var(--base0F)">{</span><span style="color: var(--base08)">contact</span>.<span style="color: var(--base08)">notes</span><span style="color: var(--base0F)">}</span></<span style="color: var(--base08)">p</span>><span style="color: var(--base0F)">}</span>
</span><span class="codeblock-line" data-line-number="46">
</span><span class="codeblock-line" data-line-number="47"> <<span style="color: var(--base08)">div</span>>
</span><span class="codeblock-line" data-line-number="48"> <<span style="color: var(--base0A)">Form</span> <span style="color: var(--base0D)">action</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">edit</span>">
</span><span class="codeblock-line" data-line-number="49"> <<span style="color: var(--base08)">button</span> <span style="color: var(--base0D)">type</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">submit</span>">Edit</<span style="color: var(--base08)">button</span>>
</span><span class="codeblock-line" data-line-number="50"> </<span style="color: var(--base0A)">Form</span>>
</span><span class="codeblock-line" data-line-number="51"> <<span style="color: var(--base0A)">Form</span>
</span><span class="codeblock-line" data-line-number="52"> <span style="color: var(--base0D)">method</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">post</span>"
</span><span class="codeblock-line" data-line-number="53"> <span style="color: var(--base0D)">action</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">destroy</span>"
</span><span class="codeblock-line" data-line-number="54"> <span style="color: var(--base0D)">onSubmit</span><span style="color: var(--base0E)">=</span><span style="color: var(--base0F)">{</span>(<span style="color: var(--base08)">event</span>) <span style="color: var(--base0D)">=></span> {
</span><span class="codeblock-line" data-line-number="55"> <span style="color: var(--base0E)">if</span> (
</span><span class="codeblock-line" data-line-number="56"> <span style="color: var(--base0E)">!</span><span style="color: var(--base0D)">confirm</span>(
</span><span class="codeblock-line" data-line-number="57"> "<span style="color: var(--base0B)">Please confirm you want to delete this record.</span>"
</span><span class="codeblock-line" data-line-number="58"> )
</span><span class="codeblock-line" data-line-number="59"> ) {
</span><span class="codeblock-line" data-line-number="60"> <span style="color: var(--base08)">event</span>.<span style="color: var(--base0D)">preventDefault</span>();
</span><span class="codeblock-line" data-line-number="61"> }
</span><span class="codeblock-line" data-line-number="62"> }<span style="color: var(--base0F)">}</span>
</span><span class="codeblock-line" data-line-number="63"> >
</span><span class="codeblock-line" data-line-number="64"> <<span style="color: var(--base08)">button</span> <span style="color: var(--base0D)">type</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">submit</span>">Delete</<span style="color: var(--base08)">button</span>>
</span><span class="codeblock-line" data-line-number="65"> </<span style="color: var(--base0A)">Form</span>>
</span><span class="codeblock-line" data-line-number="66"> </<span style="color: var(--base08)">div</span>>
</span><span class="codeblock-line" data-line-number="67"> </<span style="color: var(--base08)">div</span>>
</span><span class="codeblock-line" data-line-number="68"> </<span style="color: var(--base08)">div</span>>
</span><span class="codeblock-line" data-line-number="69"> );
</span><span class="codeblock-line" data-line-number="70">}
</span><span class="codeblock-line" data-line-number="71">
</span><span class="codeblock-line" data-line-number="72"><span style="color: var(--base0D)">function</span> <span style="color: var(--base0D)">Favorite</span>({ <span style="color: var(--base08)">contact</span> }) {
</span><span class="codeblock-line" data-line-number="73"> <span style="color: var(--base03)">// yes, this is a `let` for later</span>
</span><span class="codeblock-line" data-line-number="74"> <span style="color: var(--base0A)">let</span> <span style="color: var(--base08)">favorite</span> <span style="color: var(--base0E)">=</span> <span style="color: var(--base08)">contact</span>.<span style="color: var(--base08)">favorite</span>;
</span><span class="codeblock-line" data-line-number="75"> <span style="color: var(--base0E)">return</span> (
</span><span class="codeblock-line" data-line-number="76"> <<span style="color: var(--base0A)">Form</span> <span style="color: var(--base0D)">method</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">post</span>">
</span><span class="codeblock-line" data-line-number="77"> <<span style="color: var(--base08)">button</span>
</span><span class="codeblock-line" data-line-number="78"> <span style="color: var(--base0D)">name</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">favorite</span>"
</span><span class="codeblock-line" data-line-number="79"> <span style="color: var(--base0D)">value</span><span style="color: var(--base0E)">=</span><span style="color: var(--base0F)">{</span><span style="color: var(--base08)">favorite</span> <span style="color: var(--base0E)">?</span> "<span style="color: var(--base0B)">false</span>" <span style="color: var(--base0E)">:</span> "<span style="color: var(--base0B)">true</span>"<span style="color: var(--base0F)">}</span>
</span><span class="codeblock-line" data-line-number="80"> <span style="color: var(--base0D)">aria-label</span><span style="color: var(--base0E)">=</span><span style="color: var(--base0F)">{</span>
</span><span class="codeblock-line" data-line-number="81"> <span style="color: var(--base08)">favorite</span>
</span><span class="codeblock-line" data-line-number="82"> <span style="color: var(--base0E)">?</span> "<span style="color: var(--base0B)">Remove from favorites</span>"
</span><span class="codeblock-line" data-line-number="83"> <span style="color: var(--base0E)">:</span> "<span style="color: var(--base0B)">Add to favorites</span>"
</span><span class="codeblock-line" data-line-number="84"> <span style="color: var(--base0F)">}</span>
</span><span class="codeblock-line" data-line-number="85"> >
</span><span class="codeblock-line" data-line-number="86"> <span style="color: var(--base0F)">{</span><span style="color: var(--base08)">favorite</span> <span style="color: var(--base0E)">?</span> "<span style="color: var(--base0B)">★</span>" <span style="color: var(--base0E)">:</span> "<span style="color: var(--base0B)">☆</span>"<span style="color: var(--base0F)">}</span>
</span><span class="codeblock-line" data-line-number="87"> </<span style="color: var(--base08)">button</span>>
</span><span class="codeblock-line" data-line-number="88"> </<span style="color: var(--base0A)">Form</span>>
</span><span class="codeblock-line" data-line-number="89"> );
</span><span class="codeblock-line" data-line-number="90">}
</span></code></pre></div><p>Now that we've got a component, let's hook it up to a new route.</p><p>👉 <strong>Import the contact component and create a new route</strong></p><div><pre data-filename="main.jsx" data-line-numbers="true" data-lang="js" style="color: var(--base05);background-color: var(--base00)"><code><span class="codeblock-line" data-line-number="1"><span style="color: var(--base03)">/* existing imports */</span>
</span><span class="codeblock-line" data-highlight="true" data-line-number="2"><span style="color: var(--base0E)">import</span> <span style="color: var(--base08)">Contact</span> <span style="color: var(--base0E)">from</span> "<span style="color: var(--base0B)">./routes/contact</span>";
</span><span class="codeblock-line" data-line-number="3">
</span><span class="codeblock-line" data-line-number="4"><span style="color: var(--base0A)">const</span> <span style="color: var(--base08)">router</span> <span style="color: var(--base0E)">=</span> <span style="color: var(--base0D)">createBrowserRouter</span>([
</span><span class="codeblock-line" data-line-number="5"> {
</span><span class="codeblock-line" data-line-number="6"> path: "<span style="color: var(--base0B)">/</span>",
</span><span class="codeblock-line" data-line-number="7"> element: <<span style="color: var(--base0A)">Root</span> />,
</span><span class="codeblock-line" data-line-number="8"> errorElement: <<span style="color: var(--base0A)">ErrorPage</span> />,
</span><span class="codeblock-line" data-line-number="9"> },
</span><span class="codeblock-line" data-highlight="true" data-line-number="10"> {
</span><span class="codeblock-line" data-highlight="true" data-line-number="11"> path: "<span style="color: var(--base0B)">contacts/:contactId</span>",
</span><span class="codeblock-line" data-highlight="true" data-line-number="12"> element: <<span style="color: var(--base0A)">Contact</span> />,
</span><span class="codeblock-line" data-highlight="true" data-line-number="13"> },
</span><span class="codeblock-line" data-line-number="14">]);
</span><span class="codeblock-line" data-line-number="15">
</span><span class="codeblock-line" data-line-number="16"><span style="color: var(--base03)">/* existing code */</span>
</span></code></pre></div><p>Now if we click one of the links or visit <code>/contacts/1</code> we get our new component!</p><img class="tutorial" loading="lazy" alt="contact route rendering without the parent layout" src="./Tutorial v6.4.3 _ React Router_files/04.webp"><p>However, it's not inside of our root layout 😠</p><h2 id="nested-routes"><a href="https://reactrouter.com/en/main/start/tutorial#nested-routes" aria-hidden="true" tabindex="-1"><span class="icon icon-link"></span></a>Nested Routes</h2><p>We want the contact component to render <em>inside</em> of the <code><Root></code> layout like this.</p><img class="tutorial" loading="lazy" src="./Tutorial v6.4.3 _ React Router_files/05.webp"><p>We do it by making the contact route a <em>child</em> of the root route.</p><p>👉 <strong>Move the contacts route to be a child of the root route</strong></p><div><pre data-filename="src/main.jsx" data-line-numbers="true" data-lang="jsx" style="color: var(--base05);background-color: var(--base00)"><code><span class="codeblock-line" data-line-number="1"><span style="color: var(--base0A)">const</span> <span style="color: var(--base08)">router</span> <span style="color: var(--base0E)">=</span> <span style="color: var(--base0D)">createBrowserRouter</span>([
</span><span class="codeblock-line" data-line-number="2"> {
</span><span class="codeblock-line" data-line-number="3"> path: "<span style="color: var(--base0B)">/</span>",
</span><span class="codeblock-line" data-line-number="4"> element: <<span style="color: var(--base0A)">Root</span> />,
</span><span class="codeblock-line" data-line-number="5"> errorElement: <<span style="color: var(--base0A)">ErrorPage</span> />,
</span><span class="codeblock-line" data-highlight="true" data-line-number="6"> children: [
</span><span class="codeblock-line" data-highlight="true" data-line-number="7"> {
</span><span class="codeblock-line" data-highlight="true" data-line-number="8"> path: "<span style="color: var(--base0B)">contacts/:contactId</span>",
</span><span class="codeblock-line" data-highlight="true" data-line-number="9"> element: <<span style="color: var(--base0A)">Contact</span> />,
</span><span class="codeblock-line" data-highlight="true" data-line-number="10"> },
</span><span class="codeblock-line" data-highlight="true" data-line-number="11"> ],
</span><span class="codeblock-line" data-line-number="12"> },
</span><span class="codeblock-line" data-line-number="13">]);
</span></code></pre></div><p>You'll now see the root layout again but a blank page on the right. We need to tell the root route <em>where</em> we want it to render its child routes. We do that with <a href="https://reactrouter.com/en/main/components/outlet"><code><Outlet></code></a>.</p><p>Find the <code><div id="detail"></code> and put an outlet inside</p><p>👉 <strong>Render an <a href="https://reactrouter.com/en/main/components/outlet"><code><Outlet></code></a></strong></p><div><pre data-filename="src/routes/root.jsx" data-line-numbers="true" data-lang="jsx" style="color: var(--base05);background-color: var(--base00)"><code><span class="codeblock-line" data-highlight="true" data-line-number="1"><span style="color: var(--base0E)">import</span> { <span style="color: var(--base08)">Outlet</span> } <span style="color: var(--base0E)">from</span> "<span style="color: var(--base0B)">react-router-dom</span>";
</span><span class="codeblock-line" data-line-number="2">
</span><span class="codeblock-line" data-line-number="3"><span style="color: var(--base0E)">export</span> <span style="color: var(--base0E)">default</span> <span style="color: var(--base0D)">function</span> <span style="color: var(--base0D)">Root</span>() {
</span><span class="codeblock-line" data-line-number="4"> <span style="color: var(--base0E)">return</span> (
</span><span class="codeblock-line" data-line-number="5"> <>
</span><span class="codeblock-line" data-line-number="6"> <span style="color: var(--base0F)">{</span><span style="color: var(--base03)">/* all the other elements */</span><span style="color: var(--base0F)">}</span>
</span><span class="codeblock-line" data-line-number="7"> <<span style="color: var(--base08)">div</span> <span style="color: var(--base0D)">id</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">detail</span>">
</span><span class="codeblock-line" data-highlight="true" data-line-number="8"> <<span style="color: var(--base0A)">Outlet</span> />
</span><span class="codeblock-line" data-line-number="9"> </<span style="color: var(--base08)">div</span>>
</span><span class="codeblock-line" data-line-number="10"> </>
</span><span class="codeblock-line" data-line-number="11"> );
</span><span class="codeblock-line" data-line-number="12">}
</span></code></pre></div><h2 id="client-side-routing"><a href="https://reactrouter.com/en/main/start/tutorial#client-side-routing" aria-hidden="true" tabindex="-1"><span class="icon icon-link"></span></a>Client Side Routing</h2><p>You may or may not have noticed, but when we click the links in the sidebar, the browser is doing a full document request for the next URL instead of using React Router.</p><p>Client side routing allows our app to update the URL without requesting another document from the server. Instead, the app can immediately render new UI. Let's make it happen with <a href="https://reactrouter.com/en/main/components/link"><code><Link></code></a>.</p><p>👉 <strong>Change the sidebar <code><a href></code> to <code><Link to></code></strong></p><div><pre data-filename="src/routes/root.jsx" data-line-numbers="true" data-lang="jsx" style="color: var(--base05);background-color: var(--base00)"><code><span class="codeblock-line" data-highlight="true" data-line-number="1"><span style="color: var(--base0E)">import</span> { <span style="color: var(--base08)">Outlet</span>, <span style="color: var(--base08)">Link</span> } <span style="color: var(--base0E)">from</span> "<span style="color: var(--base0B)">react-router-dom</span>";
</span><span class="codeblock-line" data-line-number="2">
</span><span class="codeblock-line" data-line-number="3"><span style="color: var(--base0E)">export</span> <span style="color: var(--base0E)">default</span> <span style="color: var(--base0D)">function</span> <span style="color: var(--base0D)">Root</span>() {
</span><span class="codeblock-line" data-line-number="4"> <span style="color: var(--base0E)">return</span> (
</span><span class="codeblock-line" data-line-number="5"> <>
</span><span class="codeblock-line" data-line-number="6"> <<span style="color: var(--base08)">div</span> <span style="color: var(--base0D)">id</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">sidebar</span>">
</span><span class="codeblock-line" data-line-number="7"> <span style="color: var(--base0F)">{</span><span style="color: var(--base03)">/* other elements */</span><span style="color: var(--base0F)">}</span>
</span><span class="codeblock-line" data-line-number="8">
</span><span class="codeblock-line" data-line-number="9"> <<span style="color: var(--base08)">nav</span>>
</span><span class="codeblock-line" data-line-number="10"> <<span style="color: var(--base08)">ul</span>>
</span><span class="codeblock-line" data-line-number="11"> <<span style="color: var(--base08)">li</span>>
</span><span class="codeblock-line" data-highlight="true" data-line-number="12"> <<span style="color: var(--base0A)">Link</span> <span style="color: var(--base0D)">to</span><span style="color: var(--base0E)">=</span><span style="color: var(--base0F)">{</span>`<span style="color: var(--base0B)">contacts/1</span>`<span style="color: var(--base0F)">}</span>>Your Name</<span style="color: var(--base0A)">Link</span>>
</span><span class="codeblock-line" data-line-number="13"> </<span style="color: var(--base08)">li</span>>
</span><span class="codeblock-line" data-line-number="14"> <<span style="color: var(--base08)">li</span>>
</span><span class="codeblock-line" data-highlight="true" data-line-number="15"> <<span style="color: var(--base0A)">Link</span> <span style="color: var(--base0D)">to</span><span style="color: var(--base0E)">=</span><span style="color: var(--base0F)">{</span>`<span style="color: var(--base0B)">contacts/2</span>`<span style="color: var(--base0F)">}</span>>Your Friend</<span style="color: var(--base0A)">Link</span>>
</span><span class="codeblock-line" data-line-number="16"> </<span style="color: var(--base08)">li</span>>
</span><span class="codeblock-line" data-line-number="17"> </<span style="color: var(--base08)">ul</span>>
</span><span class="codeblock-line" data-line-number="18"> </<span style="color: var(--base08)">nav</span>>
</span><span class="codeblock-line" data-line-number="19">
</span><span class="codeblock-line" data-line-number="20"> <span style="color: var(--base0F)">{</span><span style="color: var(--base03)">/* other elements */</span><span style="color: var(--base0F)">}</span>
</span><span class="codeblock-line" data-line-number="21"> </<span style="color: var(--base08)">div</span>>
</span><span class="codeblock-line" data-line-number="22"> </>
</span><span class="codeblock-line" data-line-number="23"> );
</span><span class="codeblock-line" data-line-number="24">}
</span></code></pre></div><p>You can open the network tab in the browser devtools to see that it's not requesting documents anymore.</p><h2 id="loading-data"><a href="https://reactrouter.com/en/main/start/tutorial#loading-data" aria-hidden="true" tabindex="-1"><span class="icon icon-link"></span></a>Loading Data</h2><p>URL segments, layouts, and data are more often than not coupled (tripled?) together. We can see it in this app already:</p><table>
<thead>
<tr>
<th>URL Segment</th>
<th>Component</th>
<th>Data</th>
</tr>
</thead>
<tbody>
<tr>
<td>/</td>
<td><code><Root></code></td>
<td>list of contacts</td>
</tr>
<tr>
<td>contacts/:id</td>
<td><code><Contact></code></td>
<td>individual contact</td>
</tr>
</tbody>
</table><p>Because of this natural coupling, React Router has data conventions to get data into your route components easily.</p><p>There are two APIs we'll be using to load data, <a href="https://reactrouter.com/en/main/route/loader"><code>loader</code></a> and <a href="https://reactrouter.com/en/main/hooks/use-loader-data"><code>useLoaderData</code></a>. First we'll create and export a loader function in the root module, then we'll hook it up to the route. Finally, we'll access and render the data.</p><p>👉 <strong>Export a loader from <code>root.jsx</code></strong></p><div><pre data-filename="src/routes/root.jsx" data-line-numbers="true" data-lang="jsx" style="color: var(--base05);background-color: var(--base00)"><code><span class="codeblock-line" data-line-number="1"><span style="color: var(--base0E)">import</span> { <span style="color: var(--base08)">Outlet</span>, <span style="color: var(--base08)">Link</span> } <span style="color: var(--base0E)">from</span> "<span style="color: var(--base0B)">react-router-dom</span>";
</span><span class="codeblock-line" data-highlight="true" data-line-number="2"><span style="color: var(--base0E)">import</span> { <span style="color: var(--base08)">getContacts</span> } <span style="color: var(--base0E)">from</span> "<span style="color: var(--base0B)">../contacts</span>";
</span><span class="codeblock-line" data-line-number="3">
</span><span class="codeblock-line" data-highlight="true" data-line-number="4"><span style="color: var(--base0E)">export</span> <span style="color: var(--base0E)">async</span> <span style="color: var(--base0D)">function</span> <span style="color: var(--base0D)">loader</span>() {
</span><span class="codeblock-line" data-highlight="true" data-line-number="5"> <span style="color: var(--base0A)">const</span> <span style="color: var(--base08)">contacts</span> <span style="color: var(--base0E)">=</span> <span style="color: var(--base0E)">await</span> <span style="color: var(--base0D)">getContacts</span>();
</span><span class="codeblock-line" data-highlight="true" data-line-number="6"> <span style="color: var(--base0E)">return</span> { <span style="color: var(--base08)">contacts</span> };
</span><span class="codeblock-line" data-highlight="true" data-line-number="7">}
</span></code></pre></div><p>👉 <strong>Configure the loader on the route</strong></p><div><pre data-filename="src/main.jsx" data-line-numbers="true" data-lang="jsx" style="color: var(--base05);background-color: var(--base00)"><code><span class="codeblock-line" data-line-number="1"><span style="color: var(--base03)">/* other imports */</span>
</span><span class="codeblock-line" data-highlight="true" data-line-number="2"><span style="color: var(--base0E)">import</span> <span style="color: var(--base08)">Root</span>, { <span style="color: var(--base08)">loader</span> <span style="color: var(--base0E)">as</span> <span style="color: var(--base08)">rootLoader</span> } <span style="color: var(--base0E)">from</span> "<span style="color: var(--base0B)">./routes/root</span>";
</span><span class="codeblock-line" data-line-number="3">
</span><span class="codeblock-line" data-line-number="4"><span style="color: var(--base0A)">const</span> <span style="color: var(--base08)">router</span> <span style="color: var(--base0E)">=</span> <span style="color: var(--base0D)">createBrowserRouter</span>([
</span><span class="codeblock-line" data-line-number="5"> {
</span><span class="codeblock-line" data-line-number="6"> path: "<span style="color: var(--base0B)">/</span>",
</span><span class="codeblock-line" data-line-number="7"> element: <<span style="color: var(--base0A)">Root</span> />,
</span><span class="codeblock-line" data-line-number="8"> errorElement: <<span style="color: var(--base0A)">ErrorPage</span> />,
</span><span class="codeblock-line" data-highlight="true" data-line-number="9"> loader: <span style="color: var(--base08)">rootLoader</span>,
</span><span class="codeblock-line" data-line-number="10"> children: [
</span><span class="codeblock-line" data-line-number="11"> {
</span><span class="codeblock-line" data-line-number="12"> path: "<span style="color: var(--base0B)">contacts/:contactId</span>",
</span><span class="codeblock-line" data-line-number="13"> element: <<span style="color: var(--base0A)">Contact</span> />,
</span><span class="codeblock-line" data-line-number="14"> },
</span><span class="codeblock-line" data-line-number="15"> ],
</span><span class="codeblock-line" data-line-number="16"> },
</span><span class="codeblock-line" data-line-number="17">]);
</span></code></pre></div><p>👉 <strong>Access and render the data</strong></p><div><pre data-filename="src/routes/root.jsx" data-line-numbers="true" data-lang="jsx" style="color: var(--base05);background-color: var(--base00)"><code><span class="codeblock-line" data-line-number="1"><span style="color: var(--base0E)">import</span> {
</span><span class="codeblock-line" data-line-number="2"> <span style="color: var(--base08)">Outlet</span>,
</span><span class="codeblock-line" data-line-number="3"> <span style="color: var(--base08)">Link</span>,
</span><span class="codeblock-line" data-highlight="true" data-line-number="4"> <span style="color: var(--base08)">useLoaderData</span>,
</span><span class="codeblock-line" data-line-number="5">} <span style="color: var(--base0E)">from</span> "<span style="color: var(--base0B)">react-router-dom</span>";
</span><span class="codeblock-line" data-line-number="6"><span style="color: var(--base0E)">import</span> { <span style="color: var(--base08)">getContacts</span> } <span style="color: var(--base0E)">from</span> "<span style="color: var(--base0B)">../contacts</span>";
</span><span class="codeblock-line" data-line-number="7">
</span><span class="codeblock-line" data-line-number="8"><span style="color: var(--base03)">/* other code */</span>
</span><span class="codeblock-line" data-line-number="9">
</span><span class="codeblock-line" data-line-number="10"><span style="color: var(--base0E)">export</span> <span style="color: var(--base0E)">default</span> <span style="color: var(--base0D)">function</span> <span style="color: var(--base0D)">Root</span>() {
</span><span class="codeblock-line" data-highlight="true" data-line-number="11"> <span style="color: var(--base0A)">const</span> { <span style="color: var(--base08)">contacts</span> } <span style="color: var(--base0E)">=</span> <span style="color: var(--base0D)">useLoaderData</span>();
</span><span class="codeblock-line" data-line-number="12"> <span style="color: var(--base0E)">return</span> (
</span><span class="codeblock-line" data-line-number="13"> <>
</span><span class="codeblock-line" data-line-number="14"> <<span style="color: var(--base08)">div</span> <span style="color: var(--base0D)">id</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">sidebar</span>">
</span><span class="codeblock-line" data-line-number="15"> <<span style="color: var(--base08)">h1</span>>React Router Contacts</<span style="color: var(--base08)">h1</span>>
</span><span class="codeblock-line" data-line-number="16"> <span style="color: var(--base0F)">{</span><span style="color: var(--base03)">/* other code */</span><span style="color: var(--base0F)">}</span>
</span><span class="codeblock-line" data-line-number="17">
</span><span class="codeblock-line" data-line-number="18"> <<span style="color: var(--base08)">nav</span>>
</span><span class="codeblock-line" data-highlight="true" data-line-number="19"> <span style="color: var(--base0F)">{</span><span style="color: var(--base08)">contacts</span>.length <span style="color: var(--base0E)">?</span> (
</span><span class="codeblock-line" data-highlight="true" data-line-number="20"> <<span style="color: var(--base08)">ul</span>>
</span><span class="codeblock-line" data-highlight="true" data-line-number="21"> <span style="color: var(--base0F)">{</span><span style="color: var(--base08)">contacts</span>.<span style="color: var(--base0D)">map</span>((<span style="color: var(--base08)">contact</span>) <span style="color: var(--base0D)">=></span> (
</span><span class="codeblock-line" data-highlight="true" data-line-number="22"> <<span style="color: var(--base08)">li</span> <span style="color: var(--base0D)">key</span><span style="color: var(--base0E)">=</span><span style="color: var(--base0F)">{</span><span style="color: var(--base08)">contact</span>.<span style="color: var(--base08)">id</span><span style="color: var(--base0F)">}</span>>
</span><span class="codeblock-line" data-highlight="true" data-line-number="23"> <<span style="color: var(--base0A)">Link</span> <span style="color: var(--base0D)">to</span><span style="color: var(--base0E)">=</span><span style="color: var(--base0F)">{</span>`<span style="color: var(--base0B)">contacts/</span>${<span style="color: var(--base08)">contact</span>.<span style="color: var(--base08)">id</span>}`<span style="color: var(--base0F)">}</span>>
</span><span class="codeblock-line" data-highlight="true" data-line-number="24"> <span style="color: var(--base0F)">{</span><span style="color: var(--base08)">contact</span>.<span style="color: var(--base08)">first</span> <span style="color: var(--base0E)">||</span> <span style="color: var(--base08)">contact</span>.<span style="color: var(--base08)">last</span> <span style="color: var(--base0E)">?</span> (
</span><span class="codeblock-line" data-highlight="true" data-line-number="25"> <>
</span><span class="codeblock-line" data-highlight="true" data-line-number="26"> <span style="color: var(--base0F)">{</span><span style="color: var(--base08)">contact</span>.<span style="color: var(--base08)">first</span><span style="color: var(--base0F)">}</span> <span style="color: var(--base0F)">{</span><span style="color: var(--base08)">contact</span>.<span style="color: var(--base08)">last</span><span style="color: var(--base0F)">}</span>
</span><span class="codeblock-line" data-highlight="true" data-line-number="27"> </>
</span><span class="codeblock-line" data-highlight="true" data-line-number="28"> ) <span style="color: var(--base0E)">:</span> (
</span><span class="codeblock-line" data-highlight="true" data-line-number="29"> <<span style="color: var(--base08)">i</span>>No Name</<span style="color: var(--base08)">i</span>>
</span><span class="codeblock-line" data-highlight="true" data-line-number="30"> )<span style="color: var(--base0F)">}{</span>"<span style="color: var(--base0B)"> </span>"<span style="color: var(--base0F)">}</span>
</span><span class="codeblock-line" data-highlight="true" data-line-number="31"> <span style="color: var(--base0F)">{</span><span style="color: var(--base08)">contact</span>.<span style="color: var(--base08)">favorite</span> <span style="color: var(--base0E)">&&</span> <<span style="color: var(--base08)">span</span>>★</<span style="color: var(--base08)">span</span>><span style="color: var(--base0F)">}</span>
</span><span class="codeblock-line" data-highlight="true" data-line-number="32"> </<span style="color: var(--base0A)">Link</span>>
</span><span class="codeblock-line" data-highlight="true" data-line-number="33"> </<span style="color: var(--base08)">li</span>>
</span><span class="codeblock-line" data-highlight="true" data-line-number="34"> ))<span style="color: var(--base0F)">}</span>
</span><span class="codeblock-line" data-highlight="true" data-line-number="35"> </<span style="color: var(--base08)">ul</span>>
</span><span class="codeblock-line" data-highlight="true" data-line-number="36"> ) <span style="color: var(--base0E)">:</span> (
</span><span class="codeblock-line" data-highlight="true" data-line-number="37"> <<span style="color: var(--base08)">p</span>>
</span><span class="codeblock-line" data-highlight="true" data-line-number="38"> <<span style="color: var(--base08)">i</span>>No contacts</<span style="color: var(--base08)">i</span>>
</span><span class="codeblock-line" data-highlight="true" data-line-number="39"> </<span style="color: var(--base08)">p</span>>
</span><span class="codeblock-line" data-highlight="true" data-line-number="40"> )<span style="color: var(--base0F)">}</span>
</span><span class="codeblock-line" data-line-number="41"> </<span style="color: var(--base08)">nav</span>>
</span><span class="codeblock-line" data-line-number="42">
</span><span class="codeblock-line" data-line-number="43"> <span style="color: var(--base0F)">{</span><span style="color: var(--base03)">/* other code */</span><span style="color: var(--base0F)">}</span>
</span><span class="codeblock-line" data-line-number="44"> </<span style="color: var(--base08)">div</span>>
</span><span class="codeblock-line" data-line-number="45"> </>
</span><span class="codeblock-line" data-line-number="46"> );
</span><span class="codeblock-line" data-line-number="47">}
</span></code></pre></div><p>That's it! React Router will now automatically keep that data in sync with your UI. We don't have any data yet, so you're probably getting a blank list like this:</p><img class="tutorial" loading="lazy" src="./Tutorial v6.4.3 _ React Router_files/06.webp"><h2 id="data-writes--html-forms"><a href="https://reactrouter.com/en/main/start/tutorial#data-writes--html-forms" aria-hidden="true" tabindex="-1"><span class="icon icon-link"></span></a>Data Writes + HTML Forms</h2><p>We'll create our first contact in a second, but first let's talk about HTML.</p><p>React Router emulates HTML Form navigation as the data mutation primitive, according to web development before the JavaScript cambrian explosion. It gives you the UX capabilities of client rendered apps with the simplicity of the "old school" web model.</p><p>While unfamiliar to some web developers, HTML forms actually cause a navigation in the browser, just like clicking a link. The only difference is in the request: links can only change the URL while forms can also change the request method (GET vs POST) and the request body (POST form data).</p><p>Without client side routing, the browser will serialize the form's data automatically and send it to the server as the request body for POST, and as URLSearchParams for GET. React Router does the same thing, except instead of sending the request to the server, it uses client side routing and sends it to a route <a href="https://reactrouter.com/en/main/route/action"><code>action</code></a>.</p><p>We can test this out by clicking the "New" button in our app. The app should blow up because the Vite server isn't configured to handle a POST request (it sends a 404, though it should probably be a 405 🤷).</p><img class="tutorial" loading="lazy" src="./Tutorial v6.4.3 _ React Router_files/07.webp"><p>Instead of sending that POST to the Vite server to create a new contact, let's use client side routing instead.</p><h2 id="creating-contacts"><a href="https://reactrouter.com/en/main/start/tutorial#creating-contacts" aria-hidden="true" tabindex="-1"><span class="icon icon-link"></span></a>Creating Contacts</h2><p>We'll create new contacts by exporting an <code>action</code> in our root route, wiring it up to the route config, and changing our <code><form></code> to a React Router <a href="https://reactrouter.com/en/main/components/form"><code><Form></code></a>.</p><p>👉 <strong>Create the action and change <code><form></code> to <code><Form></code></strong></p><div><pre data-filename="src/routes/root.jsx" data-line-numbers="true" data-lang="jsx" style="color: var(--base05);background-color: var(--base00)"><code><span class="codeblock-line" data-line-number="1"><span style="color: var(--base0E)">import</span> {
</span><span class="codeblock-line" data-line-number="2"> <span style="color: var(--base08)">Outlet</span>,
</span><span class="codeblock-line" data-line-number="3"> <span style="color: var(--base08)">Link</span>,
</span><span class="codeblock-line" data-line-number="4"> <span style="color: var(--base08)">useLoaderData</span>,
</span><span class="codeblock-line" data-highlight="true" data-line-number="5"> <span style="color: var(--base08)">Form</span>,
</span><span class="codeblock-line" data-line-number="6">} <span style="color: var(--base0E)">from</span> "<span style="color: var(--base0B)">react-router-dom</span>";
</span><span class="codeblock-line" data-highlight="true" data-line-number="7"><span style="color: var(--base0E)">import</span> { <span style="color: var(--base08)">getContacts</span>, <span style="color: var(--base08)">createContact</span> } <span style="color: var(--base0E)">from</span> "<span style="color: var(--base0B)">../contacts</span>";
</span><span class="codeblock-line" data-line-number="8">
</span><span class="codeblock-line" data-highlight="true" data-line-number="9"><span style="color: var(--base0E)">export</span> <span style="color: var(--base0E)">async</span> <span style="color: var(--base0D)">function</span> <span style="color: var(--base0D)">action</span>() {
</span><span class="codeblock-line" data-highlight="true" data-line-number="10"> <span style="color: var(--base0E)">await</span> <span style="color: var(--base0D)">createContact</span>();
</span><span class="codeblock-line" data-highlight="true" data-line-number="11">}
</span><span class="codeblock-line" data-line-number="12">
</span><span class="codeblock-line" data-line-number="13"><span style="color: var(--base03)">/* other code */</span>
</span><span class="codeblock-line" data-line-number="14">
</span><span class="codeblock-line" data-line-number="15"><span style="color: var(--base0E)">export</span> <span style="color: var(--base0E)">default</span> <span style="color: var(--base0D)">function</span> <span style="color: var(--base0D)">Root</span>() {
</span><span class="codeblock-line" data-line-number="16"> <span style="color: var(--base0A)">const</span> { <span style="color: var(--base08)">contacts</span> } <span style="color: var(--base0E)">=</span> <span style="color: var(--base0D)">useLoaderData</span>();
</span><span class="codeblock-line" data-line-number="17"> <span style="color: var(--base0E)">return</span> (
</span><span class="codeblock-line" data-line-number="18"> <>
</span><span class="codeblock-line" data-line-number="19"> <<span style="color: var(--base08)">div</span> <span style="color: var(--base0D)">id</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">sidebar</span>">
</span><span class="codeblock-line" data-line-number="20"> <<span style="color: var(--base08)">h1</span>>React Router Contacts</<span style="color: var(--base08)">h1</span>>
</span><span class="codeblock-line" data-line-number="21"> <<span style="color: var(--base08)">div</span>>
</span><span class="codeblock-line" data-highlight="true" data-line-number="22"> <span style="color: var(--base0F)">{</span><span style="color: var(--base03)">/* other code */</span><span style="color: var(--base0F)">}</span>
</span><span class="codeblock-line" data-highlight="true" data-line-number="23"> <<span style="color: var(--base0A)">Form</span> <span style="color: var(--base0D)">method</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">post</span>">
</span><span class="codeblock-line" data-highlight="true" data-line-number="24"> <<span style="color: var(--base08)">button</span> <span style="color: var(--base0D)">type</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">submit</span>">New</<span style="color: var(--base08)">button</span>>
</span><span class="codeblock-line" data-line-number="25"> </<span style="color: var(--base0A)">Form</span>>
</span><span class="codeblock-line" data-line-number="26"> </<span style="color: var(--base08)">div</span>>
</span><span class="codeblock-line" data-line-number="27">
</span><span class="codeblock-line" data-line-number="28"> <span style="color: var(--base0F)">{</span><span style="color: var(--base03)">/* other code */</span><span style="color: var(--base0F)">}</span>
</span><span class="codeblock-line" data-line-number="29"> </<span style="color: var(--base08)">div</span>>
</span><span class="codeblock-line" data-line-number="30"> </>
</span><span class="codeblock-line" data-line-number="31"> );
</span><span class="codeblock-line" data-line-number="32">}
</span></code></pre></div><p>👉 <strong>Import and set the action on the route</strong></p><div><pre data-filename="src/main.jsx" data-line-numbers="true" data-lang="jsx" style="color: var(--base05);background-color: var(--base00)"><code><span class="codeblock-line" data-line-number="1"><span style="color: var(--base03)">/* other imports */</span>
</span><span class="codeblock-line" data-line-number="2">
</span><span class="codeblock-line" data-line-number="3"><span style="color: var(--base0E)">import</span> <span style="color: var(--base08)">Root</span>, {
</span><span class="codeblock-line" data-line-number="4"> <span style="color: var(--base08)">loader</span> <span style="color: var(--base0E)">as</span> <span style="color: var(--base08)">rootLoader</span>,
</span><span class="codeblock-line" data-highlight="true" data-line-number="5"> <span style="color: var(--base08)">action</span> <span style="color: var(--base0E)">as</span> <span style="color: var(--base08)">rootAction</span>,
</span><span class="codeblock-line" data-line-number="6">} <span style="color: var(--base0E)">from</span> "<span style="color: var(--base0B)">./routes/root</span>";
</span><span class="codeblock-line" data-line-number="7">
</span><span class="codeblock-line" data-line-number="8"><span style="color: var(--base0A)">const</span> <span style="color: var(--base08)">router</span> <span style="color: var(--base0E)">=</span> <span style="color: var(--base0D)">createBrowserRouter</span>([
</span><span class="codeblock-line" data-line-number="9"> {
</span><span class="codeblock-line" data-line-number="10"> path: "<span style="color: var(--base0B)">/</span>",
</span><span class="codeblock-line" data-line-number="11"> element: <<span style="color: var(--base0A)">Root</span> />,
</span><span class="codeblock-line" data-line-number="12"> errorElement: <<span style="color: var(--base0A)">ErrorPage</span> />,
</span><span class="codeblock-line" data-line-number="13"> loader: <span style="color: var(--base08)">rootLoader</span>,
</span><span class="codeblock-line" data-highlight="true" data-line-number="14"> action: <span style="color: var(--base08)">rootAction</span>,
</span><span class="codeblock-line" data-line-number="15"> children: [
</span><span class="codeblock-line" data-line-number="16"> {
</span><span class="codeblock-line" data-line-number="17"> path: "<span style="color: var(--base0B)">contacts/:contactId</span>",
</span><span class="codeblock-line" data-line-number="18"> element: <<span style="color: var(--base0A)">Contact</span> />,
</span><span class="codeblock-line" data-line-number="19"> },
</span><span class="codeblock-line" data-line-number="20"> ],
</span><span class="codeblock-line" data-line-number="21"> },
</span><span class="codeblock-line" data-line-number="22">]);
</span></code></pre></div><p>That's it! Go ahead and click the "New" button and you should see a new record pop into the list 🥳</p><img class="tutorial" loading="lazy" src="./Tutorial v6.4.3 _ React Router_files/08.webp"><p>The <code>createContact</code> method just creates an empty contact with no name or data or anything. But it does still create a record, promise!</p><blockquote>
<p>🧐 Wait a sec ... How did the sidebar update? Where did we call the <code>action</code>? Where's the code to refetch the data? Where are <code>useState</code>, <code>onSubmit</code> and <code>useEffect</code>?!</p>
</blockquote><p>This is where the "old school web" programming model shows up. As we discussed earlier, <a href="https://reactrouter.com/en/main/components/form"><code><Form></code></a> prevents the browser from sending the request to the server and sends it to your route <code>action</code> instead. In web semantics, a POST usually means some data is changing. By convention, React Router uses this as a hint to automatically revalidate the data on the page after the action finishes. That means all of your <code>useLoaderData</code> hooks update and the UI stays in sync with your data automatically! Pretty cool.</p><h2 id="url-params-in-loaders"><a href="https://reactrouter.com/en/main/start/tutorial#url-params-in-loaders" aria-hidden="true" tabindex="-1"><span class="icon icon-link"></span></a>URL Params in Loaders</h2><p>👉 <strong>Click on the No Name record</strong></p><p>We should be seeing our old static contact page again, with one difference: the URL now has a real ID for the record.</p><img class="tutorial" loading="lazy" src="./Tutorial v6.4.3 _ React Router_files/09.webp"><p>Reviewing the route config, the route looks like this:</p><div><pre data-line-numbers="true" data-lang="jsx" style="color: var(--base05);background-color: var(--base00)"><code><span class="codeblock-line" data-line-number="1">[
</span><span class="codeblock-line" data-line-number="2"> {
</span><span class="codeblock-line" data-line-number="3"> path: "<span style="color: var(--base0B)">contacts/:contactId</span>",
</span><span class="codeblock-line" data-line-number="4"> element: <<span style="color: var(--base0A)">Contact</span> />,
</span><span class="codeblock-line" data-line-number="5"> },
</span><span class="codeblock-line" data-line-number="6">];
</span></code></pre></div><p>Note the <code>:contactId</code> URL segment. The colon (<code>:</code>) has special meaning, turning it into a "dynamic segment". Dynamic segments will match dynamic (changing) values in that position of the URL, like the contact ID. We call these values in the URL "URL Params", or just "params" for short.</p><p>These <a href="https://reactrouter.com/en/main/route/loader#params"><code>params</code></a> are passed to the loader with keys that match the dynamic segment. For example, our segment is named <code>:contactId</code> so the value will be passed as <code>params.contactId</code>.</p><p>These params are most often used to find a record by ID. Let's try it out.</p><p>👉 <strong>Add a loader to the contact page and access data with <code>useLoaderData</code></strong></p><div><pre data-filename="src/routes/contact.jsx" data-line-numbers="true" data-lang="jsx" style="color: var(--base05);background-color: var(--base00)"><code><span class="codeblock-line" data-highlight="true" data-line-number="1"><span style="color: var(--base0E)">import</span> { <span style="color: var(--base08)">Form</span>, <span style="color: var(--base08)">useLoaderData</span> } <span style="color: var(--base0E)">from</span> "<span style="color: var(--base0B)">react-router-dom</span>";
</span><span class="codeblock-line" data-highlight="true" data-line-number="2"><span style="color: var(--base0E)">import</span> { <span style="color: var(--base08)">getContact</span> } <span style="color: var(--base0E)">from</span> "<span style="color: var(--base0B)">../contacts</span>";
</span><span class="codeblock-line" data-line-number="3">
</span><span class="codeblock-line" data-highlight="true" data-line-number="4"><span style="color: var(--base0E)">export</span> <span style="color: var(--base0E)">async</span> <span style="color: var(--base0D)">function</span> <span style="color: var(--base0D)">loader</span>({ <span style="color: var(--base08)">params</span> }) {
</span><span class="codeblock-line" data-highlight="true" data-line-number="5"> <span style="color: var(--base0E)">return</span> <span style="color: var(--base0D)">getContact</span>(<span style="color: var(--base08)">params</span>.<span style="color: var(--base08)">contactId</span>);
</span><span class="codeblock-line" data-highlight="true" data-line-number="6">}
</span><span class="codeblock-line" data-line-number="7">
</span><span class="codeblock-line" data-line-number="8"><span style="color: var(--base0E)">export</span> <span style="color: var(--base0E)">default</span> <span style="color: var(--base0D)">function</span> <span style="color: var(--base0D)">Contact</span>() {
</span><span class="codeblock-line" data-highlight="true" data-line-number="9"> <span style="color: var(--base0A)">const</span> <span style="color: var(--base08)">contact</span> <span style="color: var(--base0E)">=</span> <span style="color: var(--base0D)">useLoaderData</span>();
</span><span class="codeblock-line" data-line-number="10"> <span style="color: var(--base03)">// existing code</span>
</span><span class="codeblock-line" data-line-number="11">}
</span></code></pre></div><p>👉 <strong>Configure the loader on the route</strong></p><div><pre data-filename="src/main.jsx" data-line-numbers="true" data-lang="jsx" style="color: var(--base05);background-color: var(--base00)"><code><span class="codeblock-line" data-line-number="1"><span style="color: var(--base03)">/* existing code */</span>
</span><span class="codeblock-line" data-line-number="2"><span style="color: var(--base0E)">import</span> <span style="color: var(--base08)">Contact</span>, {
</span><span class="codeblock-line" data-highlight="true" data-line-number="3"> <span style="color: var(--base08)">loader</span> <span style="color: var(--base0E)">as</span> <span style="color: var(--base08)">contactLoader</span>,
</span><span class="codeblock-line" data-line-number="4">} <span style="color: var(--base0E)">from</span> "<span style="color: var(--base0B)">./routes/contact</span>";
</span><span class="codeblock-line" data-line-number="5">
</span><span class="codeblock-line" data-line-number="6"><span style="color: var(--base0A)">const</span> <span style="color: var(--base08)">router</span> <span style="color: var(--base0E)">=</span> <span style="color: var(--base0D)">createBrowserRouter</span>([
</span><span class="codeblock-line" data-line-number="7"> {
</span><span class="codeblock-line" data-line-number="8"> path: "<span style="color: var(--base0B)">/</span>",
</span><span class="codeblock-line" data-line-number="9"> element: <<span style="color: var(--base0A)">Root</span> />,
</span><span class="codeblock-line" data-line-number="10"> errorElement: <<span style="color: var(--base0A)">ErrorPage</span> />,
</span><span class="codeblock-line" data-line-number="11"> loader: <span style="color: var(--base08)">rootLoader</span>,
</span><span class="codeblock-line" data-line-number="12"> action: <span style="color: var(--base08)">rootAction</span>,
</span><span class="codeblock-line" data-line-number="13"> children: [
</span><span class="codeblock-line" data-line-number="14"> {
</span><span class="codeblock-line" data-line-number="15"> path: "<span style="color: var(--base0B)">contacts/:contactId</span>",
</span><span class="codeblock-line" data-line-number="16"> element: <<span style="color: var(--base0A)">Contact</span> />,
</span><span class="codeblock-line" data-highlight="true" data-line-number="17"> loader: <span style="color: var(--base08)">contactLoader</span>,
</span><span class="codeblock-line" data-line-number="18"> },
</span><span class="codeblock-line" data-line-number="19"> ],
</span><span class="codeblock-line" data-line-number="20"> },
</span><span class="codeblock-line" data-line-number="21">]);
</span><span class="codeblock-line" data-line-number="22">
</span><span class="codeblock-line" data-line-number="23"><span style="color: var(--base03)">/* existing code */</span>
</span></code></pre></div><img class="tutorial" loading="lazy" src="./Tutorial v6.4.3 _ React Router_files/10.webp"><h2 id="updating-data"><a href="https://reactrouter.com/en/main/start/tutorial#updating-data" aria-hidden="true" tabindex="-1"><span class="icon icon-link"></span></a>Updating Data</h2><p>Just like creating data, you update data with <a href="https://reactrouter.com/en/main/components/form"><code><Form></code></a>. Let's make a new route at <code>contacts/:contactId/edit</code>. Again, we'll start with the component and then wire it up to the route config.</p><p>👉 <strong>Create the edit component</strong></p><pre><code>touch src/routes/edit.jsx
</code></pre><p>👉 <strong>Add the edit page UI</strong></p><p>Nothing we haven't seen before, feel free to copy/paste:</p><div><pre data-filename="src/routes/edit.jsx" data-line-numbers="true" data-lang="jsx" style="color: var(--base05);background-color: var(--base00)"><code><span class="codeblock-line" data-line-number="1"><span style="color: var(--base0E)">import</span> { <span style="color: var(--base08)">Form</span>, <span style="color: var(--base08)">useLoaderData</span> } <span style="color: var(--base0E)">from</span> "<span style="color: var(--base0B)">react-router-dom</span>";
</span><span class="codeblock-line" data-line-number="2">
</span><span class="codeblock-line" data-line-number="3"><span style="color: var(--base0E)">export</span> <span style="color: var(--base0E)">default</span> <span style="color: var(--base0D)">function</span> <span style="color: var(--base0D)">EditContact</span>() {
</span><span class="codeblock-line" data-line-number="4"> <span style="color: var(--base0A)">const</span> <span style="color: var(--base08)">contact</span> <span style="color: var(--base0E)">=</span> <span style="color: var(--base0D)">useLoaderData</span>();
</span><span class="codeblock-line" data-line-number="5">
</span><span class="codeblock-line" data-line-number="6"> <span style="color: var(--base0E)">return</span> (
</span><span class="codeblock-line" data-line-number="7"> <<span style="color: var(--base0A)">Form</span> <span style="color: var(--base0D)">method</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">post</span>" <span style="color: var(--base0D)">id</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">contact-form</span>">
</span><span class="codeblock-line" data-line-number="8"> <<span style="color: var(--base08)">p</span>>
</span><span class="codeblock-line" data-line-number="9"> <<span style="color: var(--base08)">span</span>>Name</<span style="color: var(--base08)">span</span>>
</span><span class="codeblock-line" data-line-number="10"> <<span style="color: var(--base08)">input</span>
</span><span class="codeblock-line" data-line-number="11"> <span style="color: var(--base0D)">placeholder</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">First</span>"
</span><span class="codeblock-line" data-line-number="12"> <span style="color: var(--base0D)">aria-label</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">First name</span>"
</span><span class="codeblock-line" data-line-number="13"> <span style="color: var(--base0D)">type</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">text</span>"
</span><span class="codeblock-line" data-line-number="14"> <span style="color: var(--base0D)">name</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">first</span>"
</span><span class="codeblock-line" data-line-number="15"> <span style="color: var(--base0D)">defaultValue</span><span style="color: var(--base0E)">=</span><span style="color: var(--base0F)">{</span><span style="color: var(--base08)">contact</span>.<span style="color: var(--base08)">first</span><span style="color: var(--base0F)">}</span>
</span><span class="codeblock-line" data-line-number="16"> />
</span><span class="codeblock-line" data-line-number="17"> <<span style="color: var(--base08)">input</span>
</span><span class="codeblock-line" data-line-number="18"> <span style="color: var(--base0D)">placeholder</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">Last</span>"
</span><span class="codeblock-line" data-line-number="19"> <span style="color: var(--base0D)">aria-label</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">Last name</span>"
</span><span class="codeblock-line" data-line-number="20"> <span style="color: var(--base0D)">type</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">text</span>"
</span><span class="codeblock-line" data-line-number="21"> <span style="color: var(--base0D)">name</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">last</span>"
</span><span class="codeblock-line" data-line-number="22"> <span style="color: var(--base0D)">defaultValue</span><span style="color: var(--base0E)">=</span><span style="color: var(--base0F)">{</span><span style="color: var(--base08)">contact</span>.<span style="color: var(--base08)">last</span><span style="color: var(--base0F)">}</span>
</span><span class="codeblock-line" data-line-number="23"> />
</span><span class="codeblock-line" data-line-number="24"> </<span style="color: var(--base08)">p</span>>
</span><span class="codeblock-line" data-line-number="25"> <<span style="color: var(--base08)">label</span>>
</span><span class="codeblock-line" data-line-number="26"> <<span style="color: var(--base08)">span</span>>Twitter</<span style="color: var(--base08)">span</span>>
</span><span class="codeblock-line" data-line-number="27"> <<span style="color: var(--base08)">input</span>
</span><span class="codeblock-line" data-line-number="28"> <span style="color: var(--base0D)">type</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">text</span>"
</span><span class="codeblock-line" data-line-number="29"> <span style="color: var(--base0D)">name</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">twitter</span>"
</span><span class="codeblock-line" data-line-number="30"> <span style="color: var(--base0D)">placeholder</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">@jack</span>"
</span><span class="codeblock-line" data-line-number="31"> <span style="color: var(--base0D)">defaultValue</span><span style="color: var(--base0E)">=</span><span style="color: var(--base0F)">{</span><span style="color: var(--base08)">contact</span>.<span style="color: var(--base08)">twitter</span><span style="color: var(--base0F)">}</span>
</span><span class="codeblock-line" data-line-number="32"> />
</span><span class="codeblock-line" data-line-number="33"> </<span style="color: var(--base08)">label</span>>
</span><span class="codeblock-line" data-line-number="34"> <<span style="color: var(--base08)">label</span>>
</span><span class="codeblock-line" data-line-number="35"> <<span style="color: var(--base08)">span</span>>Avatar URL</<span style="color: var(--base08)">span</span>>
</span><span class="codeblock-line" data-line-number="36"> <<span style="color: var(--base08)">input</span>
</span><span class="codeblock-line" data-line-number="37"> <span style="color: var(--base0D)">placeholder</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">https://example.com/avatar.jpg</span>"
</span><span class="codeblock-line" data-line-number="38"> <span style="color: var(--base0D)">aria-label</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">Avatar URL</span>"
</span><span class="codeblock-line" data-line-number="39"> <span style="color: var(--base0D)">type</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">text</span>"
</span><span class="codeblock-line" data-line-number="40"> <span style="color: var(--base0D)">name</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">avatar</span>"
</span><span class="codeblock-line" data-line-number="41"> <span style="color: var(--base0D)">defaultValue</span><span style="color: var(--base0E)">=</span><span style="color: var(--base0F)">{</span><span style="color: var(--base08)">contact</span>.<span style="color: var(--base08)">avatar</span><span style="color: var(--base0F)">}</span>
</span><span class="codeblock-line" data-line-number="42"> />
</span><span class="codeblock-line" data-line-number="43"> </<span style="color: var(--base08)">label</span>>
</span><span class="codeblock-line" data-line-number="44"> <<span style="color: var(--base08)">label</span>>
</span><span class="codeblock-line" data-line-number="45"> <<span style="color: var(--base08)">span</span>>Notes</<span style="color: var(--base08)">span</span>>
</span><span class="codeblock-line" data-line-number="46"> <<span style="color: var(--base08)">textarea</span>
</span><span class="codeblock-line" data-line-number="47"> <span style="color: var(--base0D)">name</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">notes</span>"
</span><span class="codeblock-line" data-line-number="48"> <span style="color: var(--base0D)">defaultValue</span><span style="color: var(--base0E)">=</span><span style="color: var(--base0F)">{</span><span style="color: var(--base08)">contact</span>.<span style="color: var(--base08)">notes</span><span style="color: var(--base0F)">}</span>
</span><span class="codeblock-line" data-line-number="49"> <span style="color: var(--base0D)">rows</span><span style="color: var(--base0E)">=</span><span style="color: var(--base0F)">{</span><span style="color: var(--base09)">6</span><span style="color: var(--base0F)">}</span>
</span><span class="codeblock-line" data-line-number="50"> />
</span><span class="codeblock-line" data-line-number="51"> </<span style="color: var(--base08)">label</span>>
</span><span class="codeblock-line" data-line-number="52"> <<span style="color: var(--base08)">p</span>>
</span><span class="codeblock-line" data-line-number="53"> <<span style="color: var(--base08)">button</span> <span style="color: var(--base0D)">type</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">submit</span>">Save</<span style="color: var(--base08)">button</span>>
</span><span class="codeblock-line" data-line-number="54"> <<span style="color: var(--base08)">button</span> <span style="color: var(--base0D)">type</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">button</span>">Cancel</<span style="color: var(--base08)">button</span>>
</span><span class="codeblock-line" data-line-number="55"> </<span style="color: var(--base08)">p</span>>
</span><span class="codeblock-line" data-line-number="56"> </<span style="color: var(--base0A)">Form</span>>
</span><span class="codeblock-line" data-line-number="57"> );
</span><span class="codeblock-line" data-line-number="58">}
</span></code></pre></div><p>👉 <strong>Add the new edit route</strong></p><div><pre data-filename="src/main.jsx" data-line-numbers="true" data-lang="jsx" style="color: var(--base05);background-color: var(--base00)"><code><span class="codeblock-line" data-line-number="1"><span style="color: var(--base03)">/* existing code */</span>
</span><span class="codeblock-line" data-highlight="true" data-line-number="2"><span style="color: var(--base0E)">import</span> <span style="color: var(--base08)">EditContact</span> <span style="color: var(--base0E)">from</span> "<span style="color: var(--base0B)">./routes/edit</span>";
</span><span class="codeblock-line" data-line-number="3">
</span><span class="codeblock-line" data-line-number="4"><span style="color: var(--base0A)">const</span> <span style="color: var(--base08)">router</span> <span style="color: var(--base0E)">=</span> <span style="color: var(--base0D)">createBrowserRouter</span>([
</span><span class="codeblock-line" data-line-number="5"> {
</span><span class="codeblock-line" data-line-number="6"> path: "<span style="color: var(--base0B)">/</span>",
</span><span class="codeblock-line" data-line-number="7"> element: <<span style="color: var(--base0A)">Root</span> />,
</span><span class="codeblock-line" data-line-number="8"> errorElement: <<span style="color: var(--base0A)">ErrorPage</span> />,
</span><span class="codeblock-line" data-line-number="9"> loader: <span style="color: var(--base08)">rootLoader</span>,
</span><span class="codeblock-line" data-line-number="10"> action: <span style="color: var(--base08)">rootAction</span>,
</span><span class="codeblock-line" data-line-number="11"> children: [
</span><span class="codeblock-line" data-line-number="12"> {
</span><span class="codeblock-line" data-line-number="13"> path: "<span style="color: var(--base0B)">contacts/:contactId</span>",
</span><span class="codeblock-line" data-line-number="14"> element: <<span style="color: var(--base0A)">Contact</span> />,
</span><span class="codeblock-line" data-line-number="15"> loader: <span style="color: var(--base08)">contactLoader</span>,
</span><span class="codeblock-line" data-line-number="16"> },
</span><span class="codeblock-line" data-highlight="true" data-line-number="17"> {
</span><span class="codeblock-line" data-highlight="true" data-line-number="18"> path: "<span style="color: var(--base0B)">contacts/:contactId/edit</span>",
</span><span class="codeblock-line" data-highlight="true" data-line-number="19"> element: <<span style="color: var(--base0A)">EditContact</span> />,
</span><span class="codeblock-line" data-highlight="true" data-line-number="20"> loader: <span style="color: var(--base08)">contactLoader</span>,
</span><span class="codeblock-line" data-highlight="true" data-line-number="21"> },
</span><span class="codeblock-line" data-line-number="22"> ],
</span><span class="codeblock-line" data-line-number="23"> },
</span><span class="codeblock-line" data-line-number="24">]);
</span><span class="codeblock-line" data-line-number="25">
</span><span class="codeblock-line" data-line-number="26"><span style="color: var(--base03)">/* existing code */</span>
</span></code></pre></div><p>We want it to be rendered in the root route's outlet, so we made it a sibling to the existing child route.</p><p>(You might note we reused the <code>contactLoader</code> for this route. This is only because we're being lazy in the tutorial. There is no reason to attempt to share loaders among routes, they usually have their own.)</p><p>Alright, clicking the "Edit" button gives us this new UI:</p><img class="tutorial" loading="lazy" src="./Tutorial v6.4.3 _ React Router_files/11.webp"><h2 id="updating-contacts-with-formdata"><a href="https://reactrouter.com/en/main/start/tutorial#updating-contacts-with-formdata" aria-hidden="true" tabindex="-1"><span class="icon icon-link"></span></a>Updating Contacts with FormData</h2><p>The edit route we just created already renders a form. All we need to do to update the record is wire up an action to the route. The form will post to the action and the data will be automatically revalidated.</p><p>👉 <strong>Add an action to the edit module</strong></p><div><pre data-filename="src/routes/edit.jsx" data-line-numbers="true" data-lang="jsx" style="color: var(--base05);background-color: var(--base00)"><code><span class="codeblock-line" data-line-number="1"><span style="color: var(--base0E)">import</span> {
</span><span class="codeblock-line" data-line-number="2"> <span style="color: var(--base08)">Form</span>,
</span><span class="codeblock-line" data-line-number="3"> <span style="color: var(--base08)">useLoaderData</span>,
</span><span class="codeblock-line" data-highlight="true" data-line-number="4"> <span style="color: var(--base08)">redirect</span>,
</span><span class="codeblock-line" data-line-number="5">} <span style="color: var(--base0E)">from</span> "<span style="color: var(--base0B)">react-router-dom</span>";
</span><span class="codeblock-line" data-highlight="true" data-line-number="6"><span style="color: var(--base0E)">import</span> { <span style="color: var(--base08)">updateContact</span> } <span style="color: var(--base0E)">from</span> "<span style="color: var(--base0B)">../contacts</span>";
</span><span class="codeblock-line" data-line-number="7">
</span><span class="codeblock-line" data-highlight="true" data-line-number="8"><span style="color: var(--base0E)">export</span> <span style="color: var(--base0E)">async</span> <span style="color: var(--base0D)">function</span> <span style="color: var(--base0D)">action</span>({ <span style="color: var(--base08)">request</span>, <span style="color: var(--base08)">params</span> }) {
</span><span class="codeblock-line" data-highlight="true" data-line-number="9"> <span style="color: var(--base0A)">const</span> <span style="color: var(--base08)">formData</span> <span style="color: var(--base0E)">=</span> <span style="color: var(--base0E)">await</span> <span style="color: var(--base08)">request</span>.<span style="color: var(--base0D)">formData</span>();
</span><span class="codeblock-line" data-highlight="true" data-line-number="10"> <span style="color: var(--base0A)">const</span> <span style="color: var(--base08)">updates</span> <span style="color: var(--base0E)">=</span> <span style="color: var(--base08)">Object</span>.<span style="color: var(--base0D)">fromEntries</span>(<span style="color: var(--base08)">formData</span>);
</span><span class="codeblock-line" data-highlight="true" data-line-number="11"> <span style="color: var(--base0E)">await</span> <span style="color: var(--base0D)">updateContact</span>(<span style="color: var(--base08)">params</span>.<span style="color: var(--base08)">contactId</span>, <span style="color: var(--base08)">updates</span>);
</span><span class="codeblock-line" data-highlight="true" data-line-number="12"> <span style="color: var(--base0E)">return</span> <span style="color: var(--base0D)">redirect</span>(`<span style="color: var(--base0B)">/contacts/</span>${<span style="color: var(--base08)">params</span>.<span style="color: var(--base08)">contactId</span>}`);
</span><span class="codeblock-line" data-highlight="true" data-line-number="13">}
</span><span class="codeblock-line" data-line-number="14">
</span><span class="codeblock-line" data-line-number="15"><span style="color: var(--base03)">/* existing code */</span>
</span></code></pre></div><p>👉 <strong>Wire the action up to the route</strong></p><div><pre data-filename="src/routes/main.jsx" data-line-numbers="true" data-lang="jsx" style="color: var(--base05);background-color: var(--base00)"><code><span class="codeblock-line" data-line-number="1"><span style="color: var(--base03)">/* existing code */</span>
</span><span class="codeblock-line" data-line-number="2"><span style="color: var(--base0E)">import</span> <span style="color: var(--base08)">EditContact</span>, {
</span><span class="codeblock-line" data-highlight="true" data-line-number="3"> <span style="color: var(--base08)">action</span> <span style="color: var(--base0E)">as</span> <span style="color: var(--base08)">editAction</span>,
</span><span class="codeblock-line" data-line-number="4">} <span style="color: var(--base0E)">from</span> "<span style="color: var(--base0B)">./routes/edit</span>";
</span><span class="codeblock-line" data-line-number="5">
</span><span class="codeblock-line" data-line-number="6"><span style="color: var(--base0A)">const</span> <span style="color: var(--base08)">router</span> <span style="color: var(--base0E)">=</span> <span style="color: var(--base0D)">createBrowserRouter</span>([
</span><span class="codeblock-line" data-line-number="7"> {
</span><span class="codeblock-line" data-line-number="8"> path: "<span style="color: var(--base0B)">/</span>",
</span><span class="codeblock-line" data-line-number="9"> element: <<span style="color: var(--base0A)">Root</span> />,
</span><span class="codeblock-line" data-line-number="10"> errorElement: <<span style="color: var(--base0A)">ErrorPage</span> />,
</span><span class="codeblock-line" data-line-number="11"> loader: <span style="color: var(--base08)">rootLoader</span>,
</span><span class="codeblock-line" data-line-number="12"> action: <span style="color: var(--base08)">rootAction</span>,
</span><span class="codeblock-line" data-line-number="13"> children: [
</span><span class="codeblock-line" data-line-number="14"> {
</span><span class="codeblock-line" data-line-number="15"> path: "<span style="color: var(--base0B)">contacts/:contactId</span>",
</span><span class="codeblock-line" data-line-number="16"> element: <<span style="color: var(--base0A)">Contact</span> />,
</span><span class="codeblock-line" data-line-number="17"> loader: <span style="color: var(--base08)">contactLoader</span>,
</span><span class="codeblock-line" data-line-number="18"> },
</span><span class="codeblock-line" data-line-number="19"> {
</span><span class="codeblock-line" data-line-number="20"> path: "<span style="color: var(--base0B)">contacts/:contactId/edit</span>",
</span><span class="codeblock-line" data-line-number="21"> element: <<span style="color: var(--base0A)">EditContact</span> />,
</span><span class="codeblock-line" data-line-number="22"> loader: <span style="color: var(--base08)">contactLoader</span>,
</span><span class="codeblock-line" data-highlight="true" data-line-number="23"> action: <span style="color: var(--base08)">editAction</span>,
</span><span class="codeblock-line" data-line-number="24"> },
</span><span class="codeblock-line" data-line-number="25"> ],
</span><span class="codeblock-line" data-line-number="26"> },
</span><span class="codeblock-line" data-line-number="27">]);
</span><span class="codeblock-line" data-line-number="28">
</span><span class="codeblock-line" data-line-number="29"><span style="color: var(--base03)">/* existing code */</span>
</span></code></pre></div><p>Fill out the form, hit save, and you should see something like this! <small>(Except easier on the eyes and maybe less hairy.)</small></p><img class="tutorial" loading="lazy" src="./Tutorial v6.4.3 _ React Router_files/12.webp"><h2 id="mutation-discussion"><a href="https://reactrouter.com/en/main/start/tutorial#mutation-discussion" aria-hidden="true" tabindex="-1"><span class="icon icon-link"></span></a>Mutation Discussion</h2><blockquote>
<p>😑 It worked, but I have no idea what is going on here...</p>
</blockquote><p>Let's dig in a bit...</p><p>Open up <code>src/routes/edit.jsx</code> and look at the form elements. Notice how they each have a name:</p><div><pre data-filename="src/routes/edit.jsx" data-line-numbers="true" data-lang="jsx" style="color: var(--base05);background-color: var(--base00)"><code><span class="codeblock-line" data-line-number="1"><<span style="color: var(--base08)">input</span>
</span><span class="codeblock-line" data-line-number="2"> <span style="color: var(--base0D)">placeholder</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">First</span>"
</span><span class="codeblock-line" data-line-number="3"> <span style="color: var(--base0D)">aria-label</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">First name</span>"
</span><span class="codeblock-line" data-line-number="4"> <span style="color: var(--base0D)">type</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">text</span>"
</span><span class="codeblock-line" data-highlight="true" data-line-number="5"> <span style="color: var(--base0D)">name</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">first</span>"
</span><span class="codeblock-line" data-line-number="6"> <span style="color: var(--base0D)">defaultValue</span><span style="color: var(--base0E)">=</span><span style="color: var(--base0F)">{</span><span style="color: var(--base08)">contact</span>.<span style="color: var(--base08)">first</span><span style="color: var(--base0F)">}</span>
</span><span class="codeblock-line" data-line-number="7">/>
</span></code></pre></div><p>Without JavaScript, when a form is submitted, the browser will create <a href="https://developer.mozilla.org/en-US/docs/Web/API/FormData"><code>FormData</code></a> and set it as the body of the request when it sends it to the server. As mentioned before, React Router prevents that and sends the request to your action instead, including the <a href="https://developer.mozilla.org/en-US/docs/Web/API/FormData"><code>FormData</code></a>.</p><p>Each field in the form is accessible with <code>formData.get(name)</code>. For example, given the input field from above, you could access the first and last names like this:</p><div><pre data-line-numbers="true" data-lang="jsx" style="color: var(--base05);background-color: var(--base00)"><code><span class="codeblock-line" data-line-number="1"><span style="color: var(--base0E)">export</span> <span style="color: var(--base0E)">async</span> <span style="color: var(--base0D)">function</span> <span style="color: var(--base0D)">action</span>({ <span style="color: var(--base08)">request</span>, <span style="color: var(--base08)">params</span> }) {
</span><span class="codeblock-line" data-line-number="2"> <span style="color: var(--base0A)">const</span> <span style="color: var(--base08)">formData</span> <span style="color: var(--base0E)">=</span> <span style="color: var(--base0E)">await</span> <span style="color: var(--base08)">request</span>.<span style="color: var(--base0D)">formData</span>();
</span><span class="codeblock-line" data-highlight="true" data-line-number="3"> <span style="color: var(--base0A)">const</span> <span style="color: var(--base08)">firstName</span> <span style="color: var(--base0E)">=</span> <span style="color: var(--base08)">formData</span>.<span style="color: var(--base0D)">get</span>("<span style="color: var(--base0B)">first</span>");
</span><span class="codeblock-line" data-highlight="true" data-line-number="4"> <span style="color: var(--base0A)">const</span> <span style="color: var(--base08)">lastName</span> <span style="color: var(--base0E)">=</span> <span style="color: var(--base08)">formData</span>.<span style="color: var(--base0D)">get</span>("<span style="color: var(--base0B)">last</span>");
</span><span class="codeblock-line" data-line-number="5"> <span style="color: var(--base03)">// ...</span>
</span><span class="codeblock-line" data-line-number="6">}
</span></code></pre></div><p>Since we have a handful of form fields, we used <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/fromEntries"><code>Object.fromEntries</code></a> to collect them all into an object, which is exactly what our <code>updateContact</code> function wants.</p><div><pre data-line-numbers="true" data-lang="jsx" style="color: var(--base05);background-color: var(--base00)"><code><span class="codeblock-line" data-line-number="1"><span style="color: var(--base0A)">const</span> <span style="color: var(--base08)">updates</span> <span style="color: var(--base0E)">=</span> <span style="color: var(--base08)">Object</span>.<span style="color: var(--base0D)">fromEntries</span>(<span style="color: var(--base08)">formData</span>);
</span><span class="codeblock-line" data-line-number="2"><span style="color: var(--base08)">updates</span>.<span style="color: var(--base08)">first</span>; <span style="color: var(--base03)">// "Some"</span>
</span><span class="codeblock-line" data-line-number="3"><span style="color: var(--base08)">updates</span>.<span style="color: var(--base08)">last</span>; <span style="color: var(--base03)">// "Name"</span>
</span></code></pre></div><p>Aside from <code>action</code>, none of these APIs we're discussing are provided by React Router: <a href="https://developer.mozilla.org/en-US/docs/Web/API/Request"><code>request</code></a>, <a href="https://developer.mozilla.org/en-US/docs/Web/API/Request/formData"><code>request.formData</code></a>, <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/fromEntries"><code>Object.fromEntries</code></a> are all provided by the web platform.</p><p>After we finished the action, note the <a href="https://reactrouter.com/en/main/fetch/redirect"><code>redirect</code></a> at the end:</p><div><pre data-filename="src/routes/edit.jsx" data-line-numbers="true" data-lang="jsx" style="color: var(--base05);background-color: var(--base00)"><code><span class="codeblock-line" data-line-number="1"><span style="color: var(--base0E)">export</span> <span style="color: var(--base0E)">async</span> <span style="color: var(--base0D)">function</span> <span style="color: var(--base0D)">action</span>({ <span style="color: var(--base08)">request</span>, <span style="color: var(--base08)">params</span> }) {
</span><span class="codeblock-line" data-line-number="2"> <span style="color: var(--base0A)">const</span> <span style="color: var(--base08)">formData</span> <span style="color: var(--base0E)">=</span> <span style="color: var(--base0E)">await</span> <span style="color: var(--base08)">request</span>.<span style="color: var(--base0D)">formData</span>();
</span><span class="codeblock-line" data-line-number="3"> <span style="color: var(--base0A)">const</span> <span style="color: var(--base08)">updates</span> <span style="color: var(--base0E)">=</span> <span style="color: var(--base08)">Object</span>.<span style="color: var(--base0D)">fromEntries</span>(<span style="color: var(--base08)">formData</span>);
</span><span class="codeblock-line" data-line-number="4"> <span style="color: var(--base0E)">await</span> <span style="color: var(--base0D)">updateContact</span>(<span style="color: var(--base08)">params</span>.<span style="color: var(--base08)">contactId</span>, <span style="color: var(--base08)">updates</span>);
</span><span class="codeblock-line" data-highlight="true" data-line-number="5"> <span style="color: var(--base0E)">return</span> <span style="color: var(--base0D)">redirect</span>(`<span style="color: var(--base0B)">/contacts/</span>${<span style="color: var(--base08)">params</span>.<span style="color: var(--base08)">contactId</span>}`);
</span><span class="codeblock-line" data-line-number="6">}
</span></code></pre></div><p>Loaders and actions can both <a href="https://reactrouter.com/en/main/route/loader#returning-responses">return a <code>Response</code></a> (makes sense, since they received a <a href="https://developer.mozilla.org/en-US/docs/Web/API/Request"><code>Request</code></a>!). The <a href="https://reactrouter.com/en/main/fetch/redirect"><code>redirect</code></a> helper just makes it easier to return a <a href="https://developer.mozilla.org/en-US/docs/Web/API/Response">response</a> that tells the app to change locations.</p><p>Without client side routing, if a server redirected after a POST request, the new page would fetch the latest data and render. As we learned before, React Router emulates this model and automatically revalidates the data on the page after the action. That's why the sidebar automatically updates when we save the form. The extra revalidation code doesn't exist without client side routing, so it doesn't need to exist with client side routing either!</p><h2 id="redirecting-new-records-to-the-edit-page"><a href="https://reactrouter.com/en/main/start/tutorial#redirecting-new-records-to-the-edit-page" aria-hidden="true" tabindex="-1"><span class="icon icon-link"></span></a>Redirecting new records to the edit page</h2><p>Now that we know how to redirect, let's update the action that creates new contacts to redirect to the edit page:</p><p>👉 <strong>Redirect to the new record's edit page</strong></p><div><pre data-filename="src/routes/root.jsx" data-line-numbers="true" data-lang="jsx" style="color: var(--base05);background-color: var(--base00)"><code><span class="codeblock-line" data-line-number="1"><span style="color: var(--base0E)">import</span> {
</span><span class="codeblock-line" data-line-number="2"> <span style="color: var(--base08)">Outlet</span>,
</span><span class="codeblock-line" data-line-number="3"> <span style="color: var(--base08)">Link</span>,
</span><span class="codeblock-line" data-line-number="4"> <span style="color: var(--base08)">useLoaderData</span>,
</span><span class="codeblock-line" data-line-number="5"> <span style="color: var(--base08)">Form</span>,
</span><span class="codeblock-line" data-highlight="true" data-line-number="6"> <span style="color: var(--base08)">redirect</span>,
</span><span class="codeblock-line" data-line-number="7">} <span style="color: var(--base0E)">from</span> "<span style="color: var(--base0B)">react-router-dom</span>";
</span><span class="codeblock-line" data-line-number="8"><span style="color: var(--base0E)">import</span> { <span style="color: var(--base08)">getContacts</span>, <span style="color: var(--base08)">createContact</span> } <span style="color: var(--base0E)">from</span> "<span style="color: var(--base0B)">../contacts</span>";
</span><span class="codeblock-line" data-line-number="9">
</span><span class="codeblock-line" data-line-number="10"><span style="color: var(--base0E)">export</span> <span style="color: var(--base0E)">async</span> <span style="color: var(--base0D)">function</span> <span style="color: var(--base0D)">action</span>() {
</span><span class="codeblock-line" data-highlight="true" data-line-number="11"> <span style="color: var(--base0A)">const</span> <span style="color: var(--base08)">contact</span> <span style="color: var(--base0E)">=</span> <span style="color: var(--base0E)">await</span> <span style="color: var(--base0D)">createContact</span>();
</span><span class="codeblock-line" data-highlight="true" data-line-number="12"> <span style="color: var(--base0E)">return</span> <span style="color: var(--base0D)">redirect</span>(`<span style="color: var(--base0B)">/contacts/</span>${<span style="color: var(--base08)">contact</span>.<span style="color: var(--base08)">id</span>}<span style="color: var(--base0B)">/edit</span>`);
</span><span class="codeblock-line" data-line-number="13">}
</span></code></pre></div><p>Now when we click "New", we should end up on the edit page:</p><img class="tutorial" loading="lazy" src="./Tutorial v6.4.3 _ React Router_files/13.webp"><p>👉 <strong>Add a handful of records</strong></p><p>I'm going to use the stellar lineup of speakers from the first Remix Conference 😁</p><img class="tutorial" loading="lazy" src="./Tutorial v6.4.3 _ React Router_files/14.webp"><h2 id="active-link-styling"><a href="https://reactrouter.com/en/main/start/tutorial#active-link-styling" aria-hidden="true" tabindex="-1"><span class="icon icon-link"></span></a>Active Link Styling</h2><p>Now that we have a bunch of records, it's not clear which one we're looking at in the sidebar. We can use <a href="https://reactrouter.com/en/main/components/nav-link"><code>NavLink</code></a> to fix this.</p><p>👉 <strong>Use a <code>NavLink</code> in the sidebar</strong></p><div><pre data-filename="src/routes/root.jsx" data-line-numbers="true" data-lang="jsx" style="color: var(--base05);background-color: var(--base00)"><code><span class="codeblock-line" data-line-number="1"><span style="color: var(--base0E)">import</span> {
</span><span class="codeblock-line" data-line-number="2"> <span style="color: var(--base08)">Outlet</span>,
</span><span class="codeblock-line" data-highlight="true" data-line-number="3"> <span style="color: var(--base08)">NavLink</span>,
</span><span class="codeblock-line" data-line-number="4"> <span style="color: var(--base08)">useLoaderData</span>,
</span><span class="codeblock-line" data-line-number="5"> <span style="color: var(--base08)">Form</span>,
</span><span class="codeblock-line" data-line-number="6"> <span style="color: var(--base08)">redirect</span>,
</span><span class="codeblock-line" data-line-number="7">} <span style="color: var(--base0E)">from</span> "<span style="color: var(--base0B)">react-router-dom</span>";
</span><span class="codeblock-line" data-line-number="8">
</span><span class="codeblock-line" data-line-number="9"><span style="color: var(--base0E)">export</span> <span style="color: var(--base0E)">default</span> <span style="color: var(--base0D)">function</span> <span style="color: var(--base0D)">Root</span>() {
</span><span class="codeblock-line" data-line-number="10"> <span style="color: var(--base0E)">return</span> (
</span><span class="codeblock-line" data-line-number="11"> <>
</span><span class="codeblock-line" data-line-number="12"> <<span style="color: var(--base08)">div</span> <span style="color: var(--base0D)">id</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">sidebar</span>">
</span><span class="codeblock-line" data-line-number="13"> <span style="color: var(--base0F)">{</span><span style="color: var(--base03)">/* other code */</span><span style="color: var(--base0F)">}</span>
</span><span class="codeblock-line" data-line-number="14">
</span><span class="codeblock-line" data-line-number="15"> <<span style="color: var(--base08)">nav</span>>
</span><span class="codeblock-line" data-line-number="16"> <span style="color: var(--base0F)">{</span><span style="color: var(--base08)">contacts</span>.length <span style="color: var(--base0E)">?</span> (
</span><span class="codeblock-line" data-line-number="17"> <<span style="color: var(--base08)">ul</span>>
</span><span class="codeblock-line" data-line-number="18"> <span style="color: var(--base0F)">{</span><span style="color: var(--base08)">contacts</span>.<span style="color: var(--base0D)">map</span>((<span style="color: var(--base08)">contact</span>) <span style="color: var(--base0D)">=></span> (
</span><span class="codeblock-line" data-line-number="19"> <<span style="color: var(--base08)">li</span> <span style="color: var(--base0D)">key</span><span style="color: var(--base0E)">=</span><span style="color: var(--base0F)">{</span><span style="color: var(--base08)">contact</span>.<span style="color: var(--base08)">id</span><span style="color: var(--base0F)">}</span>>
</span><span class="codeblock-line" data-highlight="true" data-line-number="20"> <<span style="color: var(--base0A)">NavLink</span>
</span><span class="codeblock-line" data-highlight="true" data-line-number="21"> <span style="color: var(--base0D)">to</span><span style="color: var(--base0E)">=</span><span style="color: var(--base0F)">{</span>`<span style="color: var(--base0B)">contacts/</span>${<span style="color: var(--base08)">contact</span>.<span style="color: var(--base08)">id</span>}`<span style="color: var(--base0F)">}</span>
</span><span class="codeblock-line" data-highlight="true" data-line-number="22"> <span style="color: var(--base0D)">className</span><span style="color: var(--base0E)">=</span><span style="color: var(--base0F)">{</span>({ <span style="color: var(--base08)">isActive</span>, <span style="color: var(--base08)">isPending</span> }) <span style="color: var(--base0D)">=></span>
</span><span class="codeblock-line" data-highlight="true" data-line-number="23"> <span style="color: var(--base08)">isActive</span>
</span><span class="codeblock-line" data-highlight="true" data-line-number="24"> <span style="color: var(--base0E)">?</span> "<span style="color: var(--base0B)">active</span>"
</span><span class="codeblock-line" data-highlight="true" data-line-number="25"> <span style="color: var(--base0E)">:</span> <span style="color: var(--base08)">isPending</span>
</span><span class="codeblock-line" data-highlight="true" data-line-number="26"> <span style="color: var(--base0E)">?</span> "<span style="color: var(--base0B)">pending</span>"
</span><span class="codeblock-line" data-highlight="true" data-line-number="27"> <span style="color: var(--base0E)">:</span> ""
</span><span class="codeblock-line" data-highlight="true" data-line-number="28"> <span style="color: var(--base0F)">}</span>
</span><span class="codeblock-line" data-highlight="true" data-line-number="29"> >
</span><span class="codeblock-line" data-highlight="true" data-line-number="30"> <span style="color: var(--base0F)">{</span><span style="color: var(--base03)">/* other code */</span><span style="color: var(--base0F)">}</span>
</span><span class="codeblock-line" data-highlight="true" data-line-number="31"> </<span style="color: var(--base0A)">NavLink</span>>
</span><span class="codeblock-line" data-line-number="32"> </<span style="color: var(--base08)">li</span>>
</span><span class="codeblock-line" data-line-number="33"> ))<span style="color: var(--base0F)">}</span>
</span><span class="codeblock-line" data-line-number="34"> </<span style="color: var(--base08)">ul</span>>
</span><span class="codeblock-line" data-line-number="35"> ) <span style="color: var(--base0E)">:</span> (
</span><span class="codeblock-line" data-line-number="36"> <<span style="color: var(--base08)">p</span>><span style="color: var(--base0F)">{</span><span style="color: var(--base03)">/* other code */</span><span style="color: var(--base0F)">}</span></<span style="color: var(--base08)">p</span>>
</span><span class="codeblock-line" data-line-number="37"> )<span style="color: var(--base0F)">}</span>
</span><span class="codeblock-line" data-line-number="38"> </<span style="color: var(--base08)">nav</span>>
</span><span class="codeblock-line" data-line-number="39"> </<span style="color: var(--base08)">div</span>>
</span><span class="codeblock-line" data-line-number="40"> </>
</span><span class="codeblock-line" data-line-number="41"> );
</span><span class="codeblock-line" data-line-number="42">}
</span></code></pre></div><p>Note that we are passing a function to <code>className</code>. When the user is at the URL in the <code>NavLink</code>, then <code>isActive</code> will be true. When it's <em>about</em> to be active (the data is still loading) then <code>isPending</code> will be true. This allows us to easily indicate where the user is, as well as provide immediate feedback on links that have been clicked but we're still waiting for data to load.</p><img class="tutorial" loading="lazy" src="./Tutorial v6.4.3 _ React Router_files/15.webp"><h2 id="global-pending-ui"><a href="https://reactrouter.com/en/main/start/tutorial#global-pending-ui" aria-hidden="true" tabindex="-1"><span class="icon icon-link"></span></a>Global Pending UI</h2><p>As the user navigates the app, React Router will <em>leave the old page up</em> as data is loading for the next page. You may have noticed the app feels a little unresponsive as you click between the list. Let's provide the user with some feedback so the app doesn't feel unresponsive.</p><p>React Router is managing all of the state behind the scenes and reveals the pieces of it you need to build dynamic web apps. In this case, we'll use the <a href="https://reactrouter.com/en/main/hooks/use-navigation"><code>useNavigation</code></a> hook.</p><p>👉 <strong><code>useNavigation</code> to add global pending UI</strong></p><div><pre data-filename="src/routes/root.jsx" data-line-numbers="true" data-lang="jsx" style="color: var(--base05);background-color: var(--base00)"><code><span class="codeblock-line" data-line-number="1"><span style="color: var(--base0E)">import</span> {
</span><span class="codeblock-line" data-line-number="2"> <span style="color: var(--base03)">// existing code</span>
</span><span class="codeblock-line" data-highlight="true" data-line-number="3"> <span style="color: var(--base08)">useNavigation</span>,
</span><span class="codeblock-line" data-line-number="4">} <span style="color: var(--base0E)">from</span> "<span style="color: var(--base0B)">react-router-dom</span>";
</span><span class="codeblock-line" data-line-number="5">
</span><span class="codeblock-line" data-line-number="6"><span style="color: var(--base03)">// existing code</span>
</span><span class="codeblock-line" data-line-number="7">
</span><span class="codeblock-line" data-line-number="8"><span style="color: var(--base0E)">export</span> <span style="color: var(--base0E)">default</span> <span style="color: var(--base0D)">function</span> <span style="color: var(--base0D)">Root</span>() {
</span><span class="codeblock-line" data-line-number="9"> <span style="color: var(--base0A)">const</span> { <span style="color: var(--base08)">contacts</span> } <span style="color: var(--base0E)">=</span> <span style="color: var(--base0D)">useLoaderData</span>();
</span><span class="codeblock-line" data-highlight="true" data-line-number="10"> <span style="color: var(--base0A)">const</span> <span style="color: var(--base08)">navigation</span> <span style="color: var(--base0E)">=</span> <span style="color: var(--base0D)">useNavigation</span>();
</span><span class="codeblock-line" data-line-number="11">
</span><span class="codeblock-line" data-line-number="12"> <span style="color: var(--base0E)">return</span> (
</span><span class="codeblock-line" data-line-number="13"> <>
</span><span class="codeblock-line" data-line-number="14"> <<span style="color: var(--base08)">div</span> <span style="color: var(--base0D)">id</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">sidebar</span>"><span style="color: var(--base0F)">{</span><span style="color: var(--base03)">/* existing code */</span><span style="color: var(--base0F)">}</span></<span style="color: var(--base08)">div</span>>
</span><span class="codeblock-line" data-line-number="15"> <<span style="color: var(--base08)">div</span>
</span><span class="codeblock-line" data-line-number="16"> <span style="color: var(--base0D)">id</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">detail</span>"
</span><span class="codeblock-line" data-highlight="true" data-line-number="17"> <span style="color: var(--base0D)">className</span><span style="color: var(--base0E)">=</span><span style="color: var(--base0F)">{</span>
</span><span class="codeblock-line" data-highlight="true" data-line-number="18"> <span style="color: var(--base08)">navigation</span>.<span style="color: var(--base08)">state</span> <span style="color: var(--base0E)">===</span> "<span style="color: var(--base0B)">loading</span>" <span style="color: var(--base0E)">?</span> "<span style="color: var(--base0B)">loading</span>" <span style="color: var(--base0E)">:</span> ""
</span><span class="codeblock-line" data-highlight="true" data-line-number="19"> <span style="color: var(--base0F)">}</span>
</span><span class="codeblock-line" data-line-number="20"> >
</span><span class="codeblock-line" data-line-number="21"> <<span style="color: var(--base0A)">Outlet</span> />
</span><span class="codeblock-line" data-line-number="22"> </<span style="color: var(--base08)">div</span>>
</span><span class="codeblock-line" data-line-number="23"> </>
</span><span class="codeblock-line" data-line-number="24"> );
</span><span class="codeblock-line" data-line-number="25">}
</span></code></pre></div><p><a href="https://reactrouter.com/en/main/hooks/use-navigation"><code>useNavigation</code></a> returns the current navigation state: it can be one of <code>"idle" | "submitting" | "loading"</code>.</p><p>In our case, we add a <code>"loading"</code> class to the main part of the app if we're not idle. The CSS then adds a nice fade after a short delay (to avoid flickering the UI for fast loads). You could do anything you want though, like show a spinner or loading bar across the top.</p><img class="tutorial" loading="lazy" src="./Tutorial v6.4.3 _ React Router_files/16.webp"><p>Note that our data model (<code>src/contact.js</code>) has a clientside cache, so navigating to the same contact is fast the second time. This behavior is <em>not</em> React Router, it will re-load data for changing routes no matter if you've been there before or not. It does, however, avoid calling the loaders for <em>unchanging</em> routes (like the list) during a navigation.</p><h2 id="deleting-records"><a href="https://reactrouter.com/en/main/start/tutorial#deleting-records" aria-hidden="true" tabindex="-1"><span class="icon icon-link"></span></a>Deleting Records</h2><p>If we review code in the contact route, we can find the delete button looks like this:</p><div><pre data-filename="src/routes/contact.jsx" data-line-numbers="true" data-lang="jsx" style="color: var(--base05);background-color: var(--base00)"><code><span class="codeblock-line" data-line-number="1"><<span style="color: var(--base0A)">Form</span>
</span><span class="codeblock-line" data-line-number="2"> <span style="color: var(--base0D)">method</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">post</span>"
</span><span class="codeblock-line" data-highlight="true" data-line-number="3"> <span style="color: var(--base0D)">action</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">destroy</span>"
</span><span class="codeblock-line" data-line-number="4"> <span style="color: var(--base0D)">onSubmit</span><span style="color: var(--base0E)">=</span><span style="color: var(--base0F)">{</span>(<span style="color: var(--base08)">event</span>) <span style="color: var(--base0D)">=></span> {
</span><span class="codeblock-line" data-line-number="5"> <span style="color: var(--base0E)">if</span> (
</span><span class="codeblock-line" data-line-number="6"> <span style="color: var(--base0E)">!</span><span style="color: var(--base0D)">confirm</span>(
</span><span class="codeblock-line" data-line-number="7"> "<span style="color: var(--base0B)">Please confirm you want to delete this record.</span>"
</span><span class="codeblock-line" data-line-number="8"> )
</span><span class="codeblock-line" data-line-number="9"> ) {
</span><span class="codeblock-line" data-line-number="10"> <span style="color: var(--base08)">event</span>.<span style="color: var(--base0D)">preventDefault</span>();
</span><span class="codeblock-line" data-line-number="11"> }
</span><span class="codeblock-line" data-line-number="12"> }<span style="color: var(--base0F)">}</span>
</span><span class="codeblock-line" data-line-number="13">>
</span><span class="codeblock-line" data-line-number="14"> <<span style="color: var(--base08)">button</span> <span style="color: var(--base0D)">type</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">submit</span>">Delete</<span style="color: var(--base08)">button</span>>
</span><span class="codeblock-line" data-line-number="15"></<span style="color: var(--base0A)">Form</span>>
</span></code></pre></div><p>Note the <code>action</code> points to <code>"destroy"</code>. Like <code><Link to></code>, <code><Form action></code> can take a <em>relative</em> value. Since the form is rendered in <code>contact/:contactId</code>, then a relative action with <code>destroy</code> will submit the form to <code>contact/:contactId/destroy</code> when clicked.</p><p>At this point you should know everything you need to know to make the delete button work. Maybe give it a shot before moving on? You'll need:</p><ol>
<li>A new route</li>
<li>An <code>action</code> at that route</li>
<li><code>deleteContact</code> from <code>src/contacts.js</code></li>
</ol><p>👉 <strong>Create the "destroy" route module</strong></p><pre><code>touch src/routes/destroy.jsx
</code></pre><p>👉 <strong>Add the destroy action</strong></p><div><pre data-filename="src/routes/destroy.jsx" data-line-numbers="true" data-lang="jsx" style="color: var(--base05);background-color: var(--base00)"><code><span class="codeblock-line" data-line-number="1"><span style="color: var(--base0E)">import</span> { <span style="color: var(--base08)">redirect</span> } <span style="color: var(--base0E)">from</span> "<span style="color: var(--base0B)">react-router-dom</span>";
</span><span class="codeblock-line" data-line-number="2"><span style="color: var(--base0E)">import</span> { <span style="color: var(--base08)">deleteContact</span> } <span style="color: var(--base0E)">from</span> "<span style="color: var(--base0B)">../contacts</span>";
</span><span class="codeblock-line" data-line-number="3">
</span><span class="codeblock-line" data-line-number="4"><span style="color: var(--base0E)">export</span> <span style="color: var(--base0E)">async</span> <span style="color: var(--base0D)">function</span> <span style="color: var(--base0D)">action</span>({ <span style="color: var(--base08)">params</span> }) {
</span><span class="codeblock-line" data-line-number="5"> <span style="color: var(--base0E)">await</span> <span style="color: var(--base0D)">deleteContact</span>(<span style="color: var(--base08)">params</span>.<span style="color: var(--base08)">contactId</span>);
</span><span class="codeblock-line" data-line-number="6"> <span style="color: var(--base0E)">return</span> <span style="color: var(--base0D)">redirect</span>("<span style="color: var(--base0B)">/</span>");
</span><span class="codeblock-line" data-line-number="7">}
</span></code></pre></div><p>👉 <strong>Add the destroy route to the route config</strong></p><div><pre data-filename="src/main.jsx" data-line-numbers="true" data-lang="jsx" style="color: var(--base05);background-color: var(--base00)"><code><span class="codeblock-line" data-line-number="1"><span style="color: var(--base03)">/* existing code */</span>
</span><span class="codeblock-line" data-highlight="true" data-line-number="2"><span style="color: var(--base0E)">import</span> { <span style="color: var(--base08)">action</span> <span style="color: var(--base0E)">as</span> <span style="color: var(--base08)">destroyAction</span> } <span style="color: var(--base0E)">from</span> "<span style="color: var(--base0B)">./routes/destroy</span>";
</span><span class="codeblock-line" data-line-number="3">
</span><span class="codeblock-line" data-line-number="4"><span style="color: var(--base0A)">const</span> <span style="color: var(--base08)">router</span> <span style="color: var(--base0E)">=</span> <span style="color: var(--base0D)">createBrowserRouter</span>([
</span><span class="codeblock-line" data-line-number="5"> {
</span><span class="codeblock-line" data-line-number="6"> path: "<span style="color: var(--base0B)">/</span>",
</span><span class="codeblock-line" data-line-number="7"> <span style="color: var(--base03)">/* existing root route props */</span>
</span><span class="codeblock-line" data-line-number="8"> children: [
</span><span class="codeblock-line" data-line-number="9"> <span style="color: var(--base03)">/* existing routes */</span>
</span><span class="codeblock-line" data-highlight="true" data-line-number="10"> {
</span><span class="codeblock-line" data-highlight="true" data-line-number="11"> path: "<span style="color: var(--base0B)">contacts/:contactId/destroy</span>",
</span><span class="codeblock-line" data-highlight="true" data-line-number="12"> action: <span style="color: var(--base08)">destroyAction</span>,
</span><span class="codeblock-line" data-highlight="true" data-line-number="13"> },
</span><span class="codeblock-line" data-line-number="14"> ],
</span><span class="codeblock-line" data-line-number="15"> },
</span><span class="codeblock-line" data-line-number="16">]);
</span><span class="codeblock-line" data-line-number="17">
</span><span class="codeblock-line" data-line-number="18"><span style="color: var(--base03)">/* existing code */</span>
</span></code></pre></div><p>Alright, navigate to a record and click the "Delete" button. It works!</p><blockquote>
<p>😅 I'm still confused why this all works</p>
</blockquote><p>When the user clicks the submit button:</p><ol>
<li><code><Form></code> prevents the default browser behavior of sending a new POST request to the server, but instead emulates the browser by creating a POST request with client side routing</li>
<li>The <code><Form action="destroy"></code> matches the new route at <code>"contacts/:contactId/destroy"</code> and sends it the request</li>
<li>After the action redirects, React Router calls all of the loaders for the data on the page to get the latest values (this is "revalidation"). <code>useLoaderData</code> returns new values and causes the components to update!</li>
</ol><p>Add a form, add an action, React Router does the rest.</p><h2 id="contextual-errors"><a href="https://reactrouter.com/en/main/start/tutorial#contextual-errors" aria-hidden="true" tabindex="-1"><span class="icon icon-link"></span></a>Contextual Errors</h2><p>Just for kicks, throw an error in the destroy action:</p><div><pre data-filename="src/routes/destroy.jsx" data-line-numbers="true" data-lang="jsx" style="color: var(--base05);background-color: var(--base00)"><code><span class="codeblock-line" data-line-number="1"><span style="color: var(--base0E)">export</span> <span style="color: var(--base0E)">async</span> <span style="color: var(--base0D)">function</span> <span style="color: var(--base0D)">action</span>({ <span style="color: var(--base08)">params</span> }) {
</span><span class="codeblock-line" data-highlight="true" data-line-number="2"> <span style="color: var(--base0E)">throw</span> <span style="color: var(--base0E)">new</span> <span style="color: var(--base0D)">Error</span>("<span style="color: var(--base0B)">oh dang!</span>");
</span><span class="codeblock-line" data-line-number="3"> <span style="color: var(--base0E)">await</span> <span style="color: var(--base0D)">deleteContact</span>(<span style="color: var(--base08)">params</span>.<span style="color: var(--base08)">contactId</span>);
</span><span class="codeblock-line" data-line-number="4"> <span style="color: var(--base0E)">return</span> <span style="color: var(--base0D)">redirect</span>("<span style="color: var(--base0B)">/</span>");
</span><span class="codeblock-line" data-line-number="5">}
</span></code></pre></div><img class="tutorial" loading="lazy" src="./Tutorial v6.4.3 _ React Router_files/17.webp"><p>Recognize that screen? It's our <a href="https://reactrouter.com/en/main/route/error-element"><code>errorElement</code></a> from before. The user, however, can't really do anything to recover from this screen except to hit refresh.</p><p>Let's create a contextual error message for the destroy route:</p><div><pre data-filename="src/main.jsx" data-line-numbers="true" data-lang="jsx" style="color: var(--base05);background-color: var(--base00)"><code><span class="codeblock-line" data-line-number="1">[
</span><span class="codeblock-line" data-line-number="2"> <span style="color: var(--base03)">/* other routes */</span>
</span><span class="codeblock-line" data-line-number="3"> {
</span><span class="codeblock-line" data-line-number="4"> path: "<span style="color: var(--base0B)">contacts/:contactId/destroy</span>",
</span><span class="codeblock-line" data-line-number="5"> action: <span style="color: var(--base08)">destroyAction</span>,
</span><span class="codeblock-line" data-highlight="true" data-line-number="6"> errorElement: <<span style="color: var(--base08)">div</span>>Oops! There was an error.</<span style="color: var(--base08)">div</span>>,
</span><span class="codeblock-line" data-line-number="7"> },
</span><span class="codeblock-line" data-line-number="8">];
</span></code></pre></div><p>Now try it again:</p><img class="tutorial" loading="lazy" src="./Tutorial v6.4.3 _ React Router_files/18.webp"><p>Our user now has more options than slamming refresh, they can continue to interact with the parts of the page that aren't having trouble 🙌</p><p>Because the destroy route has its own <code>errorElement</code> and is a child of the root route, the error will render there instead of the root. As you probably noticed, these errors bubble up to the nearest <code>errorElement</code>. Add as many or as few as you like, as long as you've got one at the root.</p><h2 id="index-routes"><a href="https://reactrouter.com/en/main/start/tutorial#index-routes" aria-hidden="true" tabindex="-1"><span class="icon icon-link"></span></a>Index Routes</h2><p>When we load up the app, you'll notice a big blank page on the right side of our list.</p><img class="tutorial" loading="lazy" src="./Tutorial v6.4.3 _ React Router_files/19.webp"><p>When a route has children, and you're at the parent route's path, the <code><Outlet></code> has nothing to render because no children match. You can think of index routes as the default child route to fill in that space.</p><p>👉 <strong>Create the index route module</strong></p><pre><code>touch src/routes/index.jsx
</code></pre><p>👉 <strong>Fill in the index component's elements</strong></p><p>Feel free to copy paste, nothing special here.</p><div><pre data-filename="src/routes/index.jsx" data-line-numbers="true" data-lang="jsx" style="color: var(--base05);background-color: var(--base00)"><code><span class="codeblock-line" data-line-number="1"><span style="color: var(--base0E)">export</span> <span style="color: var(--base0E)">default</span> <span style="color: var(--base0D)">function</span> <span style="color: var(--base0D)">Index</span>() {
</span><span class="codeblock-line" data-line-number="2"> <span style="color: var(--base0E)">return</span> (
</span><span class="codeblock-line" data-line-number="3"> <<span style="color: var(--base08)">p</span> <span style="color: var(--base0D)">id</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">zero-state</span>">
</span><span class="codeblock-line" data-line-number="4"> This is a demo for React Router.
</span><span class="codeblock-line" data-line-number="5"> <<span style="color: var(--base08)">br</span> />
</span><span class="codeblock-line" data-line-number="6"> Check out<span style="color: var(--base0F)">{</span>"<span style="color: var(--base0B)"> </span>"<span style="color: var(--base0F)">}</span>
</span><span class="codeblock-line" data-line-number="7"> <<span style="color: var(--base08)">a</span> <span style="color: var(--base0D)">href</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">https://reactrouter.com/</span>">
</span><span class="codeblock-line" data-line-number="8"> the docs at reactrouter.com
</span><span class="codeblock-line" data-line-number="9"> </<span style="color: var(--base08)">a</span>>
</span><span class="codeblock-line" data-line-number="10"> .
</span><span class="codeblock-line" data-line-number="11"> </<span style="color: var(--base08)">p</span>>
</span><span class="codeblock-line" data-line-number="12"> );
</span><span class="codeblock-line" data-line-number="13">}
</span></code></pre></div><p>👉 <strong>Configure the index route</strong></p><div><pre data-filename="src/main.jsx" data-line-numbers="true" data-lang="jsx" style="color: var(--base05);background-color: var(--base00)"><code><span class="codeblock-line" data-line-number="1"><span style="color: var(--base03)">// existing code</span>
</span><span class="codeblock-line" data-highlight="true" data-line-number="2"><span style="color: var(--base0E)">import</span> <span style="color: var(--base08)">Index</span> <span style="color: var(--base0E)">from</span> "<span style="color: var(--base0B)">./routes/index</span>";
</span><span class="codeblock-line" data-line-number="3">
</span><span class="codeblock-line" data-line-number="4"><span style="color: var(--base0A)">const</span> <span style="color: var(--base08)">router</span> <span style="color: var(--base0E)">=</span> <span style="color: var(--base0D)">createBrowserRouter</span>([
</span><span class="codeblock-line" data-line-number="5"> {
</span><span class="codeblock-line" data-line-number="6"> path: "<span style="color: var(--base0B)">/</span>",
</span><span class="codeblock-line" data-line-number="7"> element: <<span style="color: var(--base0A)">Root</span> />,
</span><span class="codeblock-line" data-line-number="8"> errorElement: <<span style="color: var(--base0A)">ErrorPage</span> />,
</span><span class="codeblock-line" data-line-number="9"> loader: <span style="color: var(--base08)">rootLoader</span>,
</span><span class="codeblock-line" data-line-number="10"> action: <span style="color: var(--base08)">rootAction</span>,
</span><span class="codeblock-line" data-line-number="11"> children: [
</span><span class="codeblock-line" data-highlight="true" data-line-number="12"> { index: <span style="color: var(--base09)">true</span>, element: <<span style="color: var(--base0A)">Index</span> /> },
</span><span class="codeblock-line" data-line-number="13"> <span style="color: var(--base03)">/* existing routes */</span>
</span><span class="codeblock-line" data-line-number="14"> ],
</span><span class="codeblock-line" data-line-number="15"> },
</span><span class="codeblock-line" data-line-number="16">]);
</span></code></pre></div><p>Note the <a href="https://reactrouter.com/en/main/route/route#index"><code>{ index:true }</code></a> instead of <a href="https://reactrouter.com/en/main/route/route#path"><code>{ path: "" }</code></a>. That tells the router to match and render this route when the user is at the parent route's exact path, so there are no other child routes to render in the <code><Outlet></code>.</p><img class="tutorial" loading="lazy" src="./Tutorial v6.4.3 _ React Router_files/20.webp"><p>Voila! No more blank space. It's common to put dashboards, stats, feeds, etc. at index routes. They can participate in data loading as well.</p><h2 id="cancel-button"><a href="https://reactrouter.com/en/main/start/tutorial#cancel-button" aria-hidden="true" tabindex="-1"><span class="icon icon-link"></span></a>Cancel Button</h2><p>On the edit page we've got a cancel button that doesn't do anything yet. We'd like it to do the same thing as the browser's back button.</p><p>We'll need a click handler on the button as well as <a href="https://reactrouter.com/en/main/hooks/use-navigate"><code>useNavigate</code></a> from React Router.</p><p>👉 <strong>Add the cancel button click handler with <code>useNavigate</code></strong></p><div><pre data-filename="src/routes/edit.jsx" data-line-numbers="true" data-lang="jsx" style="color: var(--base05);background-color: var(--base00)"><code><span class="codeblock-line" data-line-number="1"><span style="color: var(--base0E)">import</span> {
</span><span class="codeblock-line" data-line-number="2"> <span style="color: var(--base08)">Form</span>,
</span><span class="codeblock-line" data-line-number="3"> <span style="color: var(--base08)">useLoaderData</span>,
</span><span class="codeblock-line" data-line-number="4"> <span style="color: var(--base08)">redirect</span>,
</span><span class="codeblock-line" data-highlight="true" data-line-number="5"> <span style="color: var(--base08)">useNavigate</span>,
</span><span class="codeblock-line" data-line-number="6">} <span style="color: var(--base0E)">from</span> "<span style="color: var(--base0B)">react-router-dom</span>";
</span><span class="codeblock-line" data-line-number="7">
</span><span class="codeblock-line" data-line-number="8"><span style="color: var(--base0E)">export</span> <span style="color: var(--base0E)">default</span> <span style="color: var(--base0D)">function</span> <span style="color: var(--base0D)">Edit</span>() {
</span><span class="codeblock-line" data-line-number="9"> <span style="color: var(--base0A)">const</span> <span style="color: var(--base08)">contact</span> <span style="color: var(--base0E)">=</span> <span style="color: var(--base0D)">useLoaderData</span>();
</span><span class="codeblock-line" data-highlight="true" data-line-number="10"> <span style="color: var(--base0A)">const</span> <span style="color: var(--base08)">navigate</span> <span style="color: var(--base0E)">=</span> <span style="color: var(--base0D)">useNavigate</span>();
</span><span class="codeblock-line" data-line-number="11">
</span><span class="codeblock-line" data-line-number="12"> <span style="color: var(--base0E)">return</span> (
</span><span class="codeblock-line" data-line-number="13"> <<span style="color: var(--base0A)">Form</span> <span style="color: var(--base0D)">method</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">post</span>" <span style="color: var(--base0D)">id</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">contact-form</span>">
</span><span class="codeblock-line" data-line-number="14"> <span style="color: var(--base0F)">{</span><span style="color: var(--base03)">/* existing code */</span><span style="color: var(--base0F)">}</span>
</span><span class="codeblock-line" data-line-number="15">
</span><span class="codeblock-line" data-line-number="16"> <<span style="color: var(--base08)">p</span>>
</span><span class="codeblock-line" data-line-number="17"> <<span style="color: var(--base08)">button</span> <span style="color: var(--base0D)">type</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">submit</span>">Save</<span style="color: var(--base08)">button</span>>
</span><span class="codeblock-line" data-line-number="18"> <<span style="color: var(--base08)">button</span>
</span><span class="codeblock-line" data-line-number="19"> <span style="color: var(--base0D)">type</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">button</span>"
</span><span class="codeblock-line" data-highlight="true" data-line-number="20"> <span style="color: var(--base0D)">onClick</span><span style="color: var(--base0E)">=</span><span style="color: var(--base0F)">{</span>() <span style="color: var(--base0D)">=></span> {
</span><span class="codeblock-line" data-highlight="true" data-line-number="21"> <span style="color: var(--base0D)">navigate</span>(<span style="color: var(--base0E)">-</span><span style="color: var(--base09)">1</span>);
</span><span class="codeblock-line" data-highlight="true" data-line-number="22"> }<span style="color: var(--base0F)">}</span>
</span><span class="codeblock-line" data-line-number="23"> >
</span><span class="codeblock-line" data-line-number="24"> Cancel
</span><span class="codeblock-line" data-line-number="25"> </<span style="color: var(--base08)">button</span>>
</span><span class="codeblock-line" data-line-number="26"> </<span style="color: var(--base08)">p</span>>
</span><span class="codeblock-line" data-line-number="27"> </<span style="color: var(--base0A)">Form</span>>
</span><span class="codeblock-line" data-line-number="28"> );
</span><span class="codeblock-line" data-line-number="29">}
</span></code></pre></div><p>Now when the user clicks "Cancel", they'll be sent back one entry in the browser's history.</p><blockquote>
<p>🧐 Why is there no <code>event.preventDefault</code> on the button?</p>
</blockquote><p>A <code><button type="button"></code>, while seemingly redundant, is the HTML way of preventing a button from submitting its form.</p><p>Two more features to go. We're on the home stretch!</p><h2 id="url-search-params-and-get-submissions"><a href="https://reactrouter.com/en/main/start/tutorial#url-search-params-and-get-submissions" aria-hidden="true" tabindex="-1"><span class="icon icon-link"></span></a>URL Search Params and GET Submissions</h2><p>All of our interactive UI so far have been either links that change the URL or forms that post data to actions. The search field is interesting because it's a mix of both: it's a form but it only changes the URL, it doesn't change data.</p><p>Right now it's just a normal HTML <code><form></code>, not a React Router <code><Form></code>. Let's see what the browser does with it by default:</p><p>👉 <strong>Type a name into the search field and hit the enter key</strong></p><p>Note the browser's URL now contains your query in the URL as <a href="https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams">URLSearchParams</a>:</p><pre><code>http://127.0.0.1:5173/?q=ryan
</code></pre><p>If we review the search form, it looks like this:</p><div><pre data-filename="src/routes/root.jsx" data-line-numbers="true" data-lang="jsx" style="color: var(--base05);background-color: var(--base00)"><code><span class="codeblock-line" data-highlight="true" data-line-number="1"><<span style="color: var(--base08)">form</span> <span style="color: var(--base0D)">id</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">search-form</span>" <span style="color: var(--base0D)">role</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">search</span>">
</span><span class="codeblock-line" data-line-number="2"> <<span style="color: var(--base08)">input</span>
</span><span class="codeblock-line" data-line-number="3"> <span style="color: var(--base0D)">id</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">q</span>"
</span><span class="codeblock-line" data-line-number="4"> <span style="color: var(--base0D)">aria-label</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">Search contacts</span>"
</span><span class="codeblock-line" data-line-number="5"> <span style="color: var(--base0D)">placeholder</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">Search</span>"
</span><span class="codeblock-line" data-line-number="6"> <span style="color: var(--base0D)">type</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">search</span>"
</span><span class="codeblock-line" data-highlight="true" data-line-number="7"> <span style="color: var(--base0D)">name</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">q</span>"
</span><span class="codeblock-line" data-line-number="8"> />
</span><span class="codeblock-line" data-line-number="9"> <<span style="color: var(--base08)">div</span> <span style="color: var(--base0D)">id</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">search-spinner</span>" <span style="color: var(--base0D)">aria-hidden</span> <span style="color: var(--base0D)">hidden</span><span style="color: var(--base0E)">=</span><span style="color: var(--base0F)">{</span><span style="color: var(--base09)">true</span><span style="color: var(--base0F)">}</span> />
</span><span class="codeblock-line" data-line-number="10"> <<span style="color: var(--base08)">div</span> <span style="color: var(--base0D)">className</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">sr-only</span>" <span style="color: var(--base0D)">aria-live</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">polite</span>"></<span style="color: var(--base08)">div</span>>
</span><span class="codeblock-line" data-line-number="11"></<span style="color: var(--base08)">form</span>>
</span></code></pre></div><p>As we've seen before, browsers can serialize forms by the <code>name</code> attribute of it's input elements. The name of this input is <code>q</code>, that's why the URL has <code>?q=</code>. If we named it <code>search</code> the URL would be <code>?search=</code>.</p><p>Note that this form is different from the others we've used, it does not have <code><form method="post"></code>. The default <code>method</code> is <code>"get"</code>. That means when the browser creates the request for the next document, it doesn't put the form data into the request POST body, but into the <a href="https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams"><code>URLSearchParams</code></a> of a GET request.</p><h2 id="get-submissions-with-client-side-routing"><a href="https://reactrouter.com/en/main/start/tutorial#get-submissions-with-client-side-routing" aria-hidden="true" tabindex="-1"><span class="icon icon-link"></span></a>GET Submissions with Client Side Routing</h2><p>Let's use client side routing to submit this form and filter the list in our existing loader.</p><p>👉 <strong>Change <code><form></code> to <code><Form></code></strong></p><div><pre data-filename="src/routes/root.jsx" data-line-numbers="true" data-lang="jsx" style="color: var(--base05);background-color: var(--base00)"><code><span class="codeblock-line" data-highlight="true" data-line-number="1"><<span style="color: var(--base0A)">Form</span> <span style="color: var(--base0D)">id</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">search-form</span>" <span style="color: var(--base0D)">role</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">search</span>">
</span><span class="codeblock-line" data-line-number="2"> <<span style="color: var(--base08)">input</span>
</span><span class="codeblock-line" data-line-number="3"> <span style="color: var(--base0D)">id</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">q</span>"
</span><span class="codeblock-line" data-line-number="4"> <span style="color: var(--base0D)">aria-label</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">Search contacts</span>"
</span><span class="codeblock-line" data-line-number="5"> <span style="color: var(--base0D)">placeholder</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">Search</span>"
</span><span class="codeblock-line" data-line-number="6"> <span style="color: var(--base0D)">type</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">search</span>"
</span><span class="codeblock-line" data-line-number="7"> <span style="color: var(--base0D)">name</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">q</span>"
</span><span class="codeblock-line" data-line-number="8"> />
</span><span class="codeblock-line" data-line-number="9"> <<span style="color: var(--base08)">div</span> <span style="color: var(--base0D)">id</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">search-spinner</span>" <span style="color: var(--base0D)">aria-hidden</span> <span style="color: var(--base0D)">hidden</span><span style="color: var(--base0E)">=</span><span style="color: var(--base0F)">{</span><span style="color: var(--base09)">true</span><span style="color: var(--base0F)">}</span> />
</span><span class="codeblock-line" data-line-number="10"> <<span style="color: var(--base08)">div</span> <span style="color: var(--base0D)">className</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">sr-only</span>" <span style="color: var(--base0D)">aria-live</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">polite</span>"></<span style="color: var(--base08)">div</span>>
</span><span class="codeblock-line" data-highlight="true" data-line-number="11"></<span style="color: var(--base0A)">Form</span>>
</span></code></pre></div><p>👉 <strong>Filter the list if there are URLSearchParams</strong></p><div><pre data-filename="src/routes/root.jsx" data-line-numbers="true" data-lang="jsx" style="color: var(--base05);background-color: var(--base00)"><code><span class="codeblock-line" data-highlight="true" data-line-number="1"><span style="color: var(--base0E)">export</span> <span style="color: var(--base0E)">async</span> <span style="color: var(--base0D)">function</span> <span style="color: var(--base0D)">loader</span>({ <span style="color: var(--base08)">request</span> }) {
</span><span class="codeblock-line" data-highlight="true" data-line-number="2"> <span style="color: var(--base0A)">const</span> <span style="color: var(--base08)">url</span> <span style="color: var(--base0E)">=</span> <span style="color: var(--base0E)">new</span> <span style="color: var(--base0D)">URL</span>(<span style="color: var(--base08)">request</span>.<span style="color: var(--base08)">url</span>);
</span><span class="codeblock-line" data-highlight="true" data-line-number="3"> <span style="color: var(--base0A)">const</span> <span style="color: var(--base08)">q</span> <span style="color: var(--base0E)">=</span> <span style="color: var(--base08)">url</span>.<span style="color: var(--base0D)">searchParams</span>.<span style="color: var(--base0D)">get</span>("<span style="color: var(--base0B)">q</span>");
</span><span class="codeblock-line" data-highlight="true" data-line-number="4"> <span style="color: var(--base0A)">const</span> <span style="color: var(--base08)">contacts</span> <span style="color: var(--base0E)">=</span> <span style="color: var(--base0E)">await</span> <span style="color: var(--base0D)">getContacts</span>(<span style="color: var(--base08)">q</span>);
</span><span class="codeblock-line" data-line-number="5"> <span style="color: var(--base0E)">return</span> { <span style="color: var(--base08)">contacts</span> };
</span><span class="codeblock-line" data-line-number="6">}
</span></code></pre></div><img class="tutorial" loading="lazy" src="./Tutorial v6.4.3 _ React Router_files/21.webp"><p>Because this is a GET, not a POST, React Router <em>does not</em> call the <code>action</code>. Submitting a GET form is the same as clicking a link: only the URL changes. That's why the code we added for filtering is in the <code>loader</code>, not the <code>action</code> of this route.</p><p>This also means it's a normal page navigation. You can click the back button to get back to where you were.</p><h2 id="synchronizing-urls-to-form-state"><a href="https://reactrouter.com/en/main/start/tutorial#synchronizing-urls-to-form-state" aria-hidden="true" tabindex="-1"><span class="icon icon-link"></span></a>Synchronizing URLs to Form State</h2><p>There are a couple of UX issues here that we can take care of quickly.</p><ol>
<li>If you click back after a search, the form field still has the value you entered even though the list is no longer filtered.</li>
<li>If you refresh the page after searching, the form field no longer has the value in it, even though the list is filtered</li>
</ol><p>In other words, the URL and our form state are out of sync.</p><p>👉 <strong>Return <code>q</code> from your loader and set it as the search field default value</strong></p><div><pre data-filename="src/routes/root.jsx" data-line-numbers="true" data-lang="jsx" style="color: var(--base05);background-color: var(--base00)"><code><span class="codeblock-line" data-line-number="1"><span style="color: var(--base03)">// existing code</span>
</span><span class="codeblock-line" data-line-number="2">
</span><span class="codeblock-line" data-line-number="3"><span style="color: var(--base0E)">export</span> <span style="color: var(--base0E)">async</span> <span style="color: var(--base0D)">function</span> <span style="color: var(--base0D)">loader</span>({ <span style="color: var(--base08)">request</span> }) {
</span><span class="codeblock-line" data-line-number="4"> <span style="color: var(--base0A)">const</span> <span style="color: var(--base08)">url</span> <span style="color: var(--base0E)">=</span> <span style="color: var(--base0E)">new</span> <span style="color: var(--base0D)">URL</span>(<span style="color: var(--base08)">request</span>.<span style="color: var(--base08)">url</span>);
</span><span class="codeblock-line" data-line-number="5"> <span style="color: var(--base0A)">const</span> <span style="color: var(--base08)">q</span> <span style="color: var(--base0E)">=</span> <span style="color: var(--base08)">url</span>.<span style="color: var(--base0D)">searchParams</span>.<span style="color: var(--base0D)">get</span>("<span style="color: var(--base0B)">q</span>");
</span><span class="codeblock-line" data-line-number="6"> <span style="color: var(--base0A)">const</span> <span style="color: var(--base08)">contacts</span> <span style="color: var(--base0E)">=</span> <span style="color: var(--base0E)">await</span> <span style="color: var(--base0D)">getContacts</span>(<span style="color: var(--base08)">q</span>);
</span><span class="codeblock-line" data-highlight="true" data-line-number="7"> <span style="color: var(--base0E)">return</span> { <span style="color: var(--base08)">contacts</span>, <span style="color: var(--base08)">q</span> };
</span><span class="codeblock-line" data-line-number="8">}
</span><span class="codeblock-line" data-line-number="9">
</span><span class="codeblock-line" data-line-number="10"><span style="color: var(--base0E)">export</span> <span style="color: var(--base0E)">default</span> <span style="color: var(--base0D)">function</span> <span style="color: var(--base0D)">Root</span>() {
</span><span class="codeblock-line" data-highlight="true" data-line-number="11"> <span style="color: var(--base0A)">const</span> { <span style="color: var(--base08)">contacts</span>, <span style="color: var(--base08)">q</span> } <span style="color: var(--base0E)">=</span> <span style="color: var(--base0D)">useLoaderData</span>();
</span><span class="codeblock-line" data-line-number="12"> <span style="color: var(--base0A)">const</span> <span style="color: var(--base08)">navigation</span> <span style="color: var(--base0E)">=</span> <span style="color: var(--base0D)">useNavigation</span>();
</span><span class="codeblock-line" data-line-number="13">
</span><span class="codeblock-line" data-line-number="14"> <span style="color: var(--base0E)">return</span> (
</span><span class="codeblock-line" data-line-number="15"> <>
</span><span class="codeblock-line" data-line-number="16"> <<span style="color: var(--base08)">div</span> <span style="color: var(--base0D)">id</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">sidebar</span>">
</span><span class="codeblock-line" data-line-number="17"> <<span style="color: var(--base08)">h1</span>>React Router Contacts</<span style="color: var(--base08)">h1</span>>
</span><span class="codeblock-line" data-line-number="18"> <<span style="color: var(--base08)">div</span>>
</span><span class="codeblock-line" data-line-number="19"> <<span style="color: var(--base0A)">Form</span> <span style="color: var(--base0D)">id</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">search-form</span>" <span style="color: var(--base0D)">role</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">search</span>">
</span><span class="codeblock-line" data-line-number="20"> <<span style="color: var(--base08)">input</span>
</span><span class="codeblock-line" data-line-number="21"> <span style="color: var(--base0D)">id</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">q</span>"
</span><span class="codeblock-line" data-line-number="22"> <span style="color: var(--base0D)">aria-label</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">Search contacts</span>"
</span><span class="codeblock-line" data-line-number="23"> <span style="color: var(--base0D)">placeholder</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">Search</span>"
</span><span class="codeblock-line" data-line-number="24"> <span style="color: var(--base0D)">type</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">search</span>"
</span><span class="codeblock-line" data-line-number="25"> <span style="color: var(--base0D)">name</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">q</span>"
</span><span class="codeblock-line" data-highlight="true" data-line-number="26"> <span style="color: var(--base0D)">defaultValue</span><span style="color: var(--base0E)">=</span><span style="color: var(--base0F)">{</span><span style="color: var(--base08)">q</span><span style="color: var(--base0F)">}</span>
</span><span class="codeblock-line" data-line-number="27"> />
</span><span class="codeblock-line" data-line-number="28"> <span style="color: var(--base0F)">{</span><span style="color: var(--base03)">/* existing code */</span><span style="color: var(--base0F)">}</span>
</span><span class="codeblock-line" data-line-number="29"> </<span style="color: var(--base0A)">Form</span>>
</span><span class="codeblock-line" data-line-number="30"> <span style="color: var(--base0F)">{</span><span style="color: var(--base03)">/* existing code */</span><span style="color: var(--base0F)">}</span>
</span><span class="codeblock-line" data-line-number="31"> </<span style="color: var(--base08)">div</span>>
</span><span class="codeblock-line" data-line-number="32"> <span style="color: var(--base0F)">{</span><span style="color: var(--base03)">/* existing code */</span><span style="color: var(--base0F)">}</span>
</span><span class="codeblock-line" data-line-number="33"> </<span style="color: var(--base08)">div</span>>
</span><span class="codeblock-line" data-line-number="34"> <span style="color: var(--base0F)">{</span><span style="color: var(--base03)">/* existing code */</span><span style="color: var(--base0F)">}</span>
</span><span class="codeblock-line" data-line-number="35"> </>
</span><span class="codeblock-line" data-line-number="36"> );
</span><span class="codeblock-line" data-line-number="37">}
</span></code></pre></div><p>That solves problem (2). If you refresh the page now, the input field will show the query.</p><img class="tutorial" loading="lazy" src="./Tutorial v6.4.3 _ React Router_files/21.webp"><p>Now for problem (1), clicking the back button and updating the input. We can bring in <code>useEffect</code> from React to manipulate the form's state in the DOM directly.</p><p>👉 <strong>Synchronize input value with the URL Search Params</strong></p><div><pre data-filename="src/routes/root.jsx" data-line-numbers="true" data-lang="jsx" style="color: var(--base05);background-color: var(--base00)"><code><span class="codeblock-line" data-highlight="true" data-line-number="1"><span style="color: var(--base0E)">import</span> { <span style="color: var(--base08)">useEffect</span> } <span style="color: var(--base0E)">from</span> "<span style="color: var(--base0B)">react</span>";
</span><span class="codeblock-line" data-line-number="2">
</span><span class="codeblock-line" data-line-number="3"><span style="color: var(--base03)">// existing code</span>
</span><span class="codeblock-line" data-line-number="4">
</span><span class="codeblock-line" data-line-number="5"><span style="color: var(--base0E)">export</span> <span style="color: var(--base0E)">default</span> <span style="color: var(--base0D)">function</span> <span style="color: var(--base0D)">Root</span>() {
</span><span class="codeblock-line" data-line-number="6"> <span style="color: var(--base0A)">const</span> { <span style="color: var(--base08)">contacts</span>, <span style="color: var(--base08)">q</span> } <span style="color: var(--base0E)">=</span> <span style="color: var(--base0D)">useLoaderData</span>();
</span><span class="codeblock-line" data-line-number="7"> <span style="color: var(--base0A)">const</span> <span style="color: var(--base08)">navigation</span> <span style="color: var(--base0E)">=</span> <span style="color: var(--base0D)">useNavigation</span>();
</span><span class="codeblock-line" data-line-number="8">
</span><span class="codeblock-line" data-highlight="true" data-line-number="9"> <span style="color: var(--base0D)">useEffect</span>(() <span style="color: var(--base0D)">=></span> {
</span><span class="codeblock-line" data-highlight="true" data-line-number="10"> <span style="color: var(--base08)">document</span>.<span style="color: var(--base0D)">getElementById</span>("<span style="color: var(--base0B)">q</span>").<span style="color: var(--base08)">value</span> <span style="color: var(--base0E)">=</span> <span style="color: var(--base08)">q</span>;
</span><span class="codeblock-line" data-highlight="true" data-line-number="11"> }, [<span style="color: var(--base08)">q</span>]);
</span><span class="codeblock-line" data-line-number="12">
</span><span class="codeblock-line" data-line-number="13"> <span style="color: var(--base03)">// existing code</span>
</span><span class="codeblock-line" data-line-number="14">}
</span></code></pre></div><blockquote>
<p>🤔 Shouldn't you use a controlled component and React State for this?</p>
</blockquote><p>You could certainly do this as a controlled component, but you'll end up with more complexity for the same behavior. You don't control the URL, the user does with the back/forward buttons. There would be more synchronization points with a controlled component.</p><details>
<summary>If you're still concerned, expand this to see what it would look like</summary><p>Notice how controlling the input requires three points of synchronization now instead of just one. The behavior is identical but the code is more complex.</p><div><pre data-filename="src/routes/root.jsx" data-line-numbers="true" data-lang="jsx" style="color: var(--base05);background-color: var(--base00)"><code><span class="codeblock-line" data-highlight="true" data-line-number="1"><span style="color: var(--base0E)">import</span> { <span style="color: var(--base08)">useEffect</span>, <span style="color: var(--base08)">useState</span> } <span style="color: var(--base0E)">from</span> "<span style="color: var(--base0B)">react</span>";
</span><span class="codeblock-line" data-line-number="2"><span style="color: var(--base03)">// existing code</span>
</span><span class="codeblock-line" data-line-number="3">
</span><span class="codeblock-line" data-line-number="4"><span style="color: var(--base0E)">export</span> <span style="color: var(--base0E)">default</span> <span style="color: var(--base0D)">function</span> <span style="color: var(--base0D)">Root</span>() {
</span><span class="codeblock-line" data-line-number="5"> <span style="color: var(--base0A)">const</span> { <span style="color: var(--base08)">contacts</span>, <span style="color: var(--base08)">q</span> } <span style="color: var(--base0E)">=</span> <span style="color: var(--base0D)">useLoaderData</span>();
</span><span class="codeblock-line" data-highlight="true" data-line-number="6"> <span style="color: var(--base0A)">const</span> [<span style="color: var(--base08)">query</span>, <span style="color: var(--base08)">setQuery</span>] <span style="color: var(--base0E)">=</span> <span style="color: var(--base0D)">useState</span>(<span style="color: var(--base08)">q</span>);
</span><span class="codeblock-line" data-line-number="7"> <span style="color: var(--base0A)">const</span> <span style="color: var(--base08)">navigation</span> <span style="color: var(--base0E)">=</span> <span style="color: var(--base0D)">useNavigation</span>();
</span><span class="codeblock-line" data-line-number="8">
</span><span class="codeblock-line" data-highlight="true" data-line-number="9"> <span style="color: var(--base0D)">useEffect</span>(() <span style="color: var(--base0D)">=></span> {
</span><span class="codeblock-line" data-highlight="true" data-line-number="10"> <span style="color: var(--base0D)">setQuery</span>(<span style="color: var(--base08)">q</span>);
</span><span class="codeblock-line" data-highlight="true" data-line-number="11"> }, [<span style="color: var(--base08)">q</span>]);
</span><span class="codeblock-line" data-line-number="12">
</span><span class="codeblock-line" data-line-number="13"> <span style="color: var(--base0E)">return</span> (
</span><span class="codeblock-line" data-line-number="14"> <>
</span><span class="codeblock-line" data-line-number="15"> <<span style="color: var(--base08)">div</span> <span style="color: var(--base0D)">id</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">sidebar</span>">
</span><span class="codeblock-line" data-line-number="16"> <<span style="color: var(--base08)">h1</span>>React Router Contacts</<span style="color: var(--base08)">h1</span>>
</span><span class="codeblock-line" data-line-number="17"> <<span style="color: var(--base08)">div</span>>
</span><span class="codeblock-line" data-line-number="18"> <<span style="color: var(--base0A)">Form</span> <span style="color: var(--base0D)">id</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">search-form</span>" <span style="color: var(--base0D)">role</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">search</span>">
</span><span class="codeblock-line" data-line-number="19"> <<span style="color: var(--base08)">input</span>
</span><span class="codeblock-line" data-line-number="20"> <span style="color: var(--base0D)">id</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">q</span>"
</span><span class="codeblock-line" data-line-number="21"> <span style="color: var(--base0D)">aria-label</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">Search contacts</span>"
</span><span class="codeblock-line" data-line-number="22"> <span style="color: var(--base0D)">placeholder</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">Search</span>"
</span><span class="codeblock-line" data-line-number="23"> <span style="color: var(--base0D)">type</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">search</span>"
</span><span class="codeblock-line" data-line-number="24"> <span style="color: var(--base0D)">name</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">q</span>"
</span><span class="codeblock-line" data-highlight="true" data-line-number="25"> <span style="color: var(--base0D)">value</span><span style="color: var(--base0E)">=</span><span style="color: var(--base0F)">{</span><span style="color: var(--base08)">query</span><span style="color: var(--base0F)">}</span>
</span><span class="codeblock-line" data-highlight="true" data-line-number="26"> <span style="color: var(--base0D)">onChange</span><span style="color: var(--base0E)">=</span><span style="color: var(--base0F)">{</span>(<span style="color: var(--base08)">e</span>) <span style="color: var(--base0D)">=></span> {
</span><span class="codeblock-line" data-highlight="true" data-line-number="27"> <span style="color: var(--base0D)">setQuery</span>(<span style="color: var(--base08)">e</span>.<span style="color: var(--base0D)">target</span>.<span style="color: var(--base08)">value</span>);
</span><span class="codeblock-line" data-highlight="true" data-line-number="28"> }<span style="color: var(--base0F)">}</span>
</span><span class="codeblock-line" data-line-number="29"> />
</span><span class="codeblock-line" data-line-number="30"> <span style="color: var(--base0F)">{</span><span style="color: var(--base03)">/* existing code */</span><span style="color: var(--base0F)">}</span>
</span><span class="codeblock-line" data-line-number="31"> </<span style="color: var(--base0A)">Form</span>>
</span><span class="codeblock-line" data-line-number="32"> <span style="color: var(--base0F)">{</span><span style="color: var(--base03)">/* existing code */</span><span style="color: var(--base0F)">}</span>
</span><span class="codeblock-line" data-line-number="33"> </<span style="color: var(--base08)">div</span>>
</span><span class="codeblock-line" data-line-number="34"> <span style="color: var(--base0F)">{</span><span style="color: var(--base03)">/* existing code */</span><span style="color: var(--base0F)">}</span>
</span><span class="codeblock-line" data-line-number="35"> </<span style="color: var(--base08)">div</span>>
</span><span class="codeblock-line" data-line-number="36"> </>
</span><span class="codeblock-line" data-line-number="37"> );
</span><span class="codeblock-line" data-line-number="38">}
</span></code></pre></div></details><h2 id="submitting-forms-onchange"><a href="https://reactrouter.com/en/main/start/tutorial#submitting-forms-onchange" aria-hidden="true" tabindex="-1"><span class="icon icon-link"></span></a>Submitting Forms <code>onChange</code></h2><p>We've got a product decision to make here. For this UI, we'd probably rather have the filtering happen on every key stroke instead of when the form is explicitly submitted.</p><p>We've seen <code>useNavigate</code> already, we'll use its cousin, <a href="https://reactrouter.com/en/main/hooks/use-submit"><code>useSubmit</code></a>, for this.</p><div><pre data-filename="src/routes/root.jsx" data-line-numbers="true" data-lang="jsx" style="color: var(--base05);background-color: var(--base00)"><code><span class="codeblock-line" data-line-number="1"><span style="color: var(--base03)">// existing code</span>
</span><span class="codeblock-line" data-line-number="2"><span style="color: var(--base0E)">import</span> {
</span><span class="codeblock-line" data-line-number="3"> <span style="color: var(--base03)">// existing code</span>
</span><span class="codeblock-line" data-highlight="true" data-line-number="4"> <span style="color: var(--base08)">useSubmit</span>,
</span><span class="codeblock-line" data-line-number="5">} <span style="color: var(--base0E)">from</span> "<span style="color: var(--base0B)">react-router-dom</span>";
</span><span class="codeblock-line" data-line-number="6">
</span><span class="codeblock-line" data-line-number="7"><span style="color: var(--base0E)">export</span> <span style="color: var(--base0E)">default</span> <span style="color: var(--base0D)">function</span> <span style="color: var(--base0D)">Root</span>() {
</span><span class="codeblock-line" data-line-number="8"> <span style="color: var(--base0A)">const</span> { <span style="color: var(--base08)">contacts</span>, <span style="color: var(--base08)">q</span> } <span style="color: var(--base0E)">=</span> <span style="color: var(--base0D)">useLoaderData</span>();
</span><span class="codeblock-line" data-line-number="9"> <span style="color: var(--base0A)">const</span> <span style="color: var(--base08)">navigation</span> <span style="color: var(--base0E)">=</span> <span style="color: var(--base0D)">useNavigation</span>();
</span><span class="codeblock-line" data-highlight="true" data-line-number="10"> <span style="color: var(--base0A)">const</span> <span style="color: var(--base08)">submit</span> <span style="color: var(--base0E)">=</span> <span style="color: var(--base0D)">useSubmit</span>();
</span><span class="codeblock-line" data-line-number="11">
</span><span class="codeblock-line" data-line-number="12"> <span style="color: var(--base0E)">return</span> (
</span><span class="codeblock-line" data-line-number="13"> <>
</span><span class="codeblock-line" data-line-number="14"> <<span style="color: var(--base08)">div</span> <span style="color: var(--base0D)">id</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">sidebar</span>">
</span><span class="codeblock-line" data-line-number="15"> <<span style="color: var(--base08)">h1</span>>React Router Contacts</<span style="color: var(--base08)">h1</span>>
</span><span class="codeblock-line" data-line-number="16"> <<span style="color: var(--base08)">div</span>>
</span><span class="codeblock-line" data-line-number="17"> <<span style="color: var(--base0A)">Form</span> <span style="color: var(--base0D)">id</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">search-form</span>" <span style="color: var(--base0D)">role</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">search</span>">
</span><span class="codeblock-line" data-line-number="18"> <<span style="color: var(--base08)">input</span>
</span><span class="codeblock-line" data-line-number="19"> <span style="color: var(--base0D)">id</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">q</span>"
</span><span class="codeblock-line" data-line-number="20"> <span style="color: var(--base0D)">aria-label</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">Search contacts</span>"
</span><span class="codeblock-line" data-line-number="21"> <span style="color: var(--base0D)">placeholder</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">Search</span>"
</span><span class="codeblock-line" data-line-number="22"> <span style="color: var(--base0D)">type</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">search</span>"
</span><span class="codeblock-line" data-line-number="23"> <span style="color: var(--base0D)">name</span><span style="color: var(--base0E)">=</span>"<span style="color: var(--base0B)">q</span>"
</span><span class="codeblock-line" data-line-number="24"> <span style="color: var(--base0D)">defaultValue</span><span style="color: var(--base0E)">=</span><span style="color: var(--base0F)">{</span><span style="color: var(--base08)">q</span><span style="color: var(--base0F)">}</span>
</span><span class="codeblock-line" data-highlight="true" data-line-number="25"> <span style="color: var(--base0D)">onChange</span><span style="color: var(--base0E)">=</span><span style="color: var(--base0F)">{</span>(<span style="color: var(--base08)">event</span>) <span style="color: var(--base0D)">=></span> {
</span><span class="codeblock-line" data-highlight="true" data-line-number="26"> <span style="color: var(--base0D)">submit</span>(<span style="color: var(--base08)">event</span>.<span style="color: var(--base0D)">currentTarget</span>.<span style="color: var(--base08)">form</span>);
</span><span class="codeblock-line" data-highlight="true" data-line-number="27"> }<span style="color: var(--base0F)">}</span>
</span><span class="codeblock-line" data-line-number="28"> />
</span><span class="codeblock-line" data-line-number="29"> <span style="color: var(--base0F)">{</span><span style="color: var(--base03)">/* existing code */</span><span style="color: var(--base0F)">}</span>
</span><span class="codeblock-line" data-line-number="30"> </<span style="color: var(--base0A)">Form</span>>
</span><span class="codeblock-line" data-line-number="31"> <span style="color: var(--base0F)">{</span><span style="color: var(--base03)">/* existing code */</span><span style="color: var(--base0F)">}</span>
</span><span class="codeblock-line" data-line-number="32"> </<span style="color: var(--base08)">div</span>>
</span><span class="codeblock-line" data-line-number="33"> <span style="color: var(--base0F)">{</span><span style="color: var(--base03)">/* existing code */</span><span style="color: var(--base0F)">}</span>
</span><span class="codeblock-line" data-line-number="34"> </<span style="color: var(--base08)">div</span>>
</span><span class="codeblock-line" data-line-number="35"> <span style="color: var(--base0F)">{</span><span style="color: var(--base03)">/* existing code */</span><span style="color: var(--base0F)">}</span>
</span><span class="codeblock-line" data-line-number="36"> </>
</span><span class="codeblock-line" data-line-number="37"> );
</span><span class="codeblock-line" data-line-number="38">}
</span></code></pre></div><p>Now as you type, the form is submitted automatically!</p><p>Note the argument to <a href="https://reactrouter.com/en/main/hooks/use-submit"><code>submit</code></a>. We're passing in <code>event.currentTarget.form</code>. The <code>currentTarget</code> is the DOM node the event is attached to, and the <code>currentTarget.form</code> is the input's parent form node. The <code>submit</code> function will serialize and submit any form you pass to it.</p><h2 id="adding-search-spinner"><a href="https://reactrouter.com/en/main/start/tutorial#adding-search-spinner" aria-hidden="true" tabindex="-1"><span class="icon icon-link"></span></a>Adding Search Spinner</h2><p>In a production app, it's likely this search will be looking for records in a database that is too large to send all at once and filter client side. That's why this demo has some faked network latency.</p><p>Without any loading indicator, the search feels kinda sluggish. Even if we could make our database faster, we'll always have the user's network latency in the way and out of our control. For a better UX, let's add some immediate UI feedback for the search. For this we'll use <a href="https://reactrouter.com/en/main/hooks/use-navigation"><code>useNavigation</code></a> again.</p><p>👉 <strong>Add the search spinner</strong></p><div><pre data-filename="src/routes/root.jsx" data-line-numbers="true" data-lang="jsx" style="color: var(--base05);background-color: var(--base00)"><code><span class="codeblock-line" data-line-number="1"><span style="color: var(--base03)">// existing code</span>
</span><span class="codeblock-line" data-line-number="2">