EN
React - own light SweetAlert2 implementation
9 points
In this short article, we would like to show how in React create its own light SweeAlert2 implementation.

The presented solution displays five types of alerts:
- success,
- error,
- question,
- info,
- warning.
The main advantage of this approach is a small alert component size, e.g.:
- SweeAlert2 in min version has 14.9kB for js and 4.2kB for css files (link here).
- own light SweetAlert2 implementation has 5.39kB for the version with removed white characters only (min version is smaller).
Practical example:
xxxxxxxxxx
1
const coverStyle = {
2
position: 'fixed',
3
left: '0',
4
top: '0',
5
right: '0',
6
bottom: '0',
7
background: 'rgba(0, 0, 0, 0.4)',
8
display: 'flex',
9
zIndex: '1000'
10
};
11
12
const coverVisibleStyle = {
13
coverStyle,
14
filter: 'opacity(1)',
15
transition: '0.2s all'
16
};
17
18
const coverHiddenStyle = {
19
coverStyle,
20
filter: 'opacity(0)',
21
transition: '0.2s all'
22
};
23
24
const alertStyle = {
25
margin: 'auto',
26
padding: '1.25em',
27
border: 'none',
28
borderRadius: '5px',
29
background: '#fff',
30
boxSizing: 'border-box',
31
width: '250px',
32
maxWidth: '100%',
33
fontFamily: '"Open Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", Helvetica, Arial, sans-serif',
34
fontSize: '0.8rem'
35
};
36
37
const alertVisibleStyle = {
38
alertStyle,
39
transform: 'scale(1.0)',
40
transition: '0.1s all'
41
};
42
43
const alertHiddenStyle = {
44
alertStyle,
45
transform: 'scale(0.4)',
46
transition: '0.1s all'
47
};
48
49
const blockStyle = {
50
padding: '0.8em 1.1em',
51
textAlign: 'center'
52
};
53
54
const iconStyle = {
55
margin: 0,
56
width: '70px',
57
height: '70px'
58
};
59
60
const titleStyle = {
61
padding: '0 1.1em',
62
textAlign: 'center',
63
textTransform: 'none',
64
wordWrap: 'break-word',
65
fontSize: '1.875em',
66
fontWeight: '600',
67
color: '#595959'
68
};
69
70
const messageStyle = {
71
padding: '0.5em 1.1em',
72
textAlign: 'center',
73
color: '#545454'
74
};
75
76
const buttonStyle = {
77
padding: '0.5em 1.1em',
78
border: '0',
79
borderRadius: '0.25em',
80
background: '#2778c4',
81
fontSize: '1.0625em',
82
fontWeight: '500',
83
color: '#fff',
84
cursor: 'pointer'
85
};
86
87
const OkIcon = props => (
88
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 22.061 22.061" {props}>
89
<path fill="#e4f4da" d="M11.03 0A11.03 11.03 0 000 11.03a11.03 11.03 0 0011.03 11.03 11.03 11.03 0 0011.03-11.03A11.03 11.03 0 0011.03 0zm0 .985A10.046 10.046 0 0121.076 11.03 10.046 10.046 0 0111.03 21.076 10.046 10.046 0 01.984 11.03 10.046 10.046 0 0111.03.985z" />
90
<path fill="#a5dc86" d="M17.187 6.123l-7.716 7.753-3.653-3.67c-.057-.054-.098-.035-.134.002l-.918.913c-.059.062-.029.09.008.13L9.47 15.97l8.747-8.789c.05-.048.038-.096.003-.133l-.92-.918c-.051-.048-.076-.042-.113-.008z" />
91
</svg>
92
);
93
94
const ErrorIcon = props => (
95
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 22.061 22.061" {props}>
96
<g fillOpacity={0.976} fill="#f27474">
97
<path d="M11.03 0A11.03 11.03 0 000 11.03a11.03 11.03 0 0011.03 11.03 11.03 11.03 0 0011.03-11.03A11.03 11.03 0 0011.03 0zm0 .985A10.046 10.046 0 0121.076 11.03 10.046 10.046 0 0111.03 21.076 10.046 10.046 0 01.984 11.03 10.046 10.046 0 0111.03.985z" />
98
<path d="M6.855 6.086l-.2.201a.48.48 0 000 .681l3.847 3.849-3.848 3.848a.48.48 0 000 .681l.201.201a.48.48 0 00.682 0l3.848-3.848 3.848 3.848a.48.48 0 00.682 0l.2-.2a.48.48 0 000-.682l-3.847-3.848 3.848-3.849a.48.48 0 000-.681l-.201-.201a.48.48 0 00-.682 0l-3.848 3.848-3.848-3.848c-.25-.204-.51-.155-.682 0z" />
99
</g>
100
</svg>
101
);
102
103
const QuestionIcon = props => (
104
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 22.061 22.061" {props}>
105
<path fill="#c9dae1" d="M11.03 0A11.03 11.03 0 000 11.03a11.03 11.03 0 0011.03 11.03 11.03 11.03 0 0011.03-11.03A11.03 11.03 0 0011.03 0zm0 .985A10.046 10.046 0 0121.076 11.03 10.046 10.046 0 0111.03 21.076 10.046 10.046 0 01.984 11.03 10.046 10.046 0 0111.03.985z" />
106
<path fill="#87adbd" d="M7.979 6.885c.703-.29 1.756-.549 2.7-.549 1.57 0 2.346.516 2.346 1.802 0 1.959-2.92 2.473-2.92 5.272v1.03h.98v-1.03c0-2.472 3.03-2.747 3.03-5.323 0-1.837-1.385-2.66-3.382-2.66-1.145 0-2.365.309-3.068.583zm2.625 8.912c-.462 0-.776.24-.776.67 0 .428.314.687.776.687.444 0 .74-.259.74-.687 0-.43-.296-.67-.74-.67z" />
107
</svg>
108
);
109
110
const InfoIcon = props => (
111
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 22.061 22.061" {props}>
112
<path fill="#9de0f6" d="M11.055-.023A11.018 11.032 0 00.036 11.01a11.018 11.032 0 0011.019 11.032A11.018 11.032 0 0022.073 11.01 11.018 11.032 0 0011.055-.023zm0 .985A10.035 10.048 0 0121.09 11.01a10.035 10.048 0 01-10.035 10.048A10.035 10.048 0 011.019 11.01 10.035 10.048 0 0111.055.962z" />
113
<path fill="#3fc3ee" d="M10.435 8.925h1.204v8.258h-1.204z" />
114
<ellipse cx={11.041} cy={6.66} rx={0.705} ry={0.819} fill="#3fc3ee" />
115
</svg>
116
);
117
118
const WarningIcon = props => (
119
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 22.061 22.061" {props}>
120
<path fill="#facea8" d="M11.055.34A11.018 10.85 0 00.036 11.19a11.018 10.85 0 0011.019 10.851 11.018 10.85 0 0011.018-10.85A11.018 10.85 0 0011.055.34zm0 .969A10.035 9.883 0 0121.09 11.19a10.035 9.883 0 01-10.035 9.883A10.035 9.883 0 011.019 11.19a10.035 9.883 0 0110.036-9.88z" />
121
<path fill="#f8bb86" d="M11.672 6.208h-1.54l.384 7.723.743.001zM11.851 16.058a.936 1.026 0 01-.936 1.026.936 1.026 0 01-.936-1.026.936 1.026 0 01.936-1.026.936 1.026 0 01.936 1.026z" />
122
</svg>
123
);
124
125
const iconTypes = {
126
'success': OkIcon,
127
'error': ErrorIcon,
128
'question': QuestionIcon,
129
'info': InfoIcon,
130
'warning': WarningIcon
131
};
132
133
const SweetAlert2Light = ({
134
visible,
135
type = 'success',
136
title,
137
message,
138
buttons = ['OK'],
139
onClose
140
}) => {
141
const [localVisible, setLocalVisible] = React.useState(false);
142
const [alertRemoved, setAlertRemoved] = React.useState(visible === false);
143
React.useEffect(() => {
144
if (visible) {
145
setLocalVisible(false);
146
setAlertRemoved(false);
147
}
148
const handle = setTimeout(() => setLocalVisible(visible), 100);
149
return () => clearTimeout(handle);
150
}, [visible]);
151
if (alertRemoved) {
152
return null;
153
}
154
const handleTransitionEnd = () => {
155
if (localVisible === false) {
156
setAlertRemoved(true);
157
}
158
};
159
const coverStyle = localVisible ? coverVisibleStyle : coverHiddenStyle;
160
const alertStyle = localVisible ? alertVisibleStyle : alertHiddenStyle;
161
const Icon = iconTypes[type];
162
return ReactDOM.createPortal(
163
<div style={coverStyle} onTransitionEnd={handleTransitionEnd}>
164
<div style={alertStyle}>
165
<div style={blockStyle}>
166
<Icon style={iconStyle} />
167
</div>
168
<div style={titleStyle}>{title}</div>
169
<div style={messageStyle}>{message}</div>
170
<div style={blockStyle}>
171
{buttons.map((button, index) => {
172
const handleClick = () => onClose(button);
173
return (
174
<a key={button}>
175
{index > 0 && ' '}
176
<button style={buttonStyle} onClick={handleClick}>
177
{button}
178
</button>
179
</a>
180
);
181
})}
182
</div>
183
</div>
184
</div>,
185
document.body
186
);
187
};
188
189
// Usage example:
190
191
const App = () => {
192
const [alertType, setAlertType] = React.useState(null);
193
const handleClose = (button) => {
194
console.log(`Button '${button}' clicked.`);
195
setAlertType(null);
196
};
197
return (
198
<div style={{ height: '300px' }}>
199
<button onClick={() => setAlertType('success')}>Show success alert</button>{' '}
200
<button onClick={() => setAlertType('error')}>Show error alert</button>{' '}
201
<button onClick={() => setAlertType('question')}>Show question alert</button>{' '}
202
<button onClick={() => setAlertType('info')}>Show info alert</button>{' '}
203
<button onClick={() => setAlertType('warning')}>Show warning alert</button>
204
<SweetAlert2Light
205
visible={alertType === 'success'}
206
type="success"
207
title="Good job!"
208
message="You clicked the button!"
209
onClose={handleClose}
210
/>
211
<SweetAlert2Light
212
visible={alertType === 'error'}
213
type="error"
214
title="Operation error!"
215
message="Operation will be repeated!"
216
onClose={handleClose}
217
/>
218
<SweetAlert2Light
219
visible={alertType === 'question'}
220
type="question"
221
title="Overwriting the file"
222
message="Do you want to continue?"
223
buttons={['Yes', 'No']}
224
onClose={handleClose}
225
/>
226
<SweetAlert2Light
227
visible={alertType === 'info'}
228
type="info"
229
title="You just clicked on a button"
230
onClose={handleClose}
231
/>
232
<SweetAlert2Light
233
visible={alertType === 'warning'}
234
type="warning"
235
title="File removing!"
236
message="Do you want to do it?"
237
buttons={['Yes', 'No']}
238
onClose={handleClose}
239
/>
240
</div>
241
);
242
};
243
244
245
const root = document.querySelector('#root');
246
ReactDOM.render(<App />, root);